jQuery Mobile Gallery

jQuery Mobile Gallery est une galerie de sites web mobiles développés, vous l’aurez deviné, avec la librairie jQueryMobile. Je vous conseille d’aller y faire un tour si vous être intéressé par cette librairie prometteuse.

On peut déjà y trouver une bonne trentaine d’exemples de site mobiles qui pourront sûrement vous aider à vous donner un bon aperçu de ce vous pouvez faire avec jQueryMobile. Un coup d’oeil au code source vous aidera à mettre rapidement le pied à l’étrier si vous souhaitez créer votre propre site mobile.

Vous pouvez visiter la galerie à l’adresse suivante: http://www.jqmgallery.com

Sortie de jQueryMobile en version 1.0alpha3 et son grand frère jQuery en version 1.5

Ces derniers jours ont été prolifiques en news au sujet de l’écosystème jQuery. Le 31 janvier est sortie la version 1.5.0 de la librairie qui nous propose différentes améliorations intéressantes et sûrement attendues depuis longtemps par bon nombre de développeurs.

Les changements principaux de cette version concernent une réécriture complète du support Ajax fournissant une API encore plus efficace et consistante, et l’intégration d’un mécanisme qui permet de chaîner différents callbacks sur des appels de fonctions qu’ils soient asynchrones ou non.

L’API Ajax fournit maintenant un object jqXHR retourné par l’appel de la méthode jQuery.ajax() qui permet de gérer de façon consistante l’objet XMLHttpRequest quelque soit la plateforme utilisée. Par exemple, il est maintenant possible d’annuler facilement une requête JSONP ce qui n’était pas le cas auparavant.

// Assign handlers immediately after making the request,
// and remember the jxhr object for this request
var jxhr = $.ajax({ url: "example.php" })
.success(function() { alert("success"); })
.error(function() { alert("error"); })
.complete(function() { alert("complete"); });

// perform other work here ...

// Set another completion function for the request above
jxhr.complete(function(){ alert("second complete"); });

Une explication complète du mécanisme appelé: ‘Deferred Objects‘ est disponible sur le site de jQuery.

jQuery expose maintenant, par le biais de la méthode jQuery.sub() un moyen simple de créer et modifier un clone de jQuery dans la même page sans pour autant interférer avec la déclaration par défaut. il est ainsi possible d’overrider des méthodes natives jQuery sans craindre d’incompatibilité avec d’autres scripts d’une même page basée sur jQuery. Cela rend aisé l’écriture d’API basées sur jQuery sans risquer une collision de namespaces.

(function(){
var sub$ = jQuery.sub();

sub$.fn.myCustomMethod = function(){
      return 'just for me';
    };

sub$(document).ready(function() {
      sub$('body').myCustomMethod() // 'just for me'
    });
})();

typeof jQuery('body').myCustomMethod // undefined

Cette version inclut également des améliorations de performance puisque la gestion de certaines méthodes de traversée de l’arbre DOM ont largement été optimisées comme les méthodes: .children(), .prev(), and .next(). Ces améliorations sont particulièrement importantes sous webkit (Chrome et Safari), mais elles sont également intéressantes sous Internet Explorer, puisqu’elles permettent de faire décoller un peu les performances de notre cher navigateur.

A ce jour quelques 4500 tests passent avec succès sur un nombre impressionnant de browsers (dont IE6, mais également Firebox 4 beta), ce qui pousse au respect au vue de l’hétérogénéité de ce joyeux petit monde.

Pour finir sur une note intéressante, le système de build de jQuery est maintenant basé sur l’environnement serveur JavaScript: NodeJS, ce qui est plutôt original, mais qui permet au navigateur de réduire sa dépendance envers Java/Rhino, et de se tourner vers de nouvelles solutions JavaScript innovantes.

Une road map de la librairie est disponible à l’adresse suivante: http://docs.jquery.com/Roadmap.

Presque 3 longs mois après la sortie de la version précédente, jQuery Mobile vient de sortir sa troisième version alpha, accompagnant à une semaine d’intervalle la release de son grand frère jQuery en version 1.5.0. L’attente ne fut pas vaine, puisque de nombreuses améliorations sont au rendez-vous:

  • Quelques 150 bugs fixés, environs 250 nouveaux tests unitaires
  • Une amélioration substantielle de la navigation et du core de la librairie
  • Un support de première classe pour de nouveaux browsers mobiles: Firefox Mobile (Fennec), Opera Mobile / Mini. Un support de première classe est également proche pour les plateformes Windows Phone 7 et Nokia.
  • Un support amélioré des browsers des plateformes iOS, Android, BlackBerry 6, Palm WebOS, et de nose browsers de bureau.
  • La prise en charge d’un date picker, non packagé dans la version. La prise en charge de nouveaux composants devrait arriver dans les prochaines versions (Progress Bar, Spinner, Time Picket)
  • La prise en charge du clavier a été améliorée et permet de mieux simuler un comportement natif au sein du navigateur.
  • De très nombreuses autres améliorations ont été intégrées à cette version. Une liste exhaustive peut être trouvée sur le blog de jQuery Mobile.

Ce qu’il faut retenir également, c’est l’annonce d’une bêta dans le mois qui va suivre, puis la sortie de la release 1.0 dans la foulée. Les objectifs fixés sont d’améliorer les performances générales de la librairie,
d’améliorer l’expérience utilisateur et la réactivité, et de fournir un support à un nombre plus important de browsers et plateformes mobiles. Bien que le sujet ne soit pas vraiment abordé dans le billet de l’annonce de cette sortie, on devrait également voir dans un avenir proche, une meilleure prise en charge de nos chères tablettes: iPad, PlayBook, et compagnie (après la sortie de la 1.0 ?).

Bien que l’annonce de cette release soit réjouissante pour le monde des développeurs mobiles, il n’en reste pas moins vrai que le niveau de performance souffre encore fortement la comparaison avec les applications natives (Gestion de liste), et la gestion de la navigation entre les différents écrans présente encore quelques dysfonctionnements. Il n’y a pas de doute que ce type de défauts seront rapidement gommés avec la sortie des prochaines versions.

Vous pouvez retrouvez ces informations sur le site de jQuery Mobile, leur blog et les suivre sur twitter via le compte: @jquerymobile.

Créer un composant Apache Camel de connexion à l’APNS – 3 sur 3

Nous avons vu dans un premier article comment initier le développement d’un composant Apache Camel, puis dans un second comment implémenter ses différentes classes.

A ce stade de notre développement nous sommes déjà en mesure d’utiliser pleinement notre composant, mais nous ne pouvons pas encore en assurer sa qualité. Pour cela, il est nécessaire d’ajouter à notre composant différentes classes de test. Bien que la testabilité de frameworks d’intégration puisse parfois paraître difficile, le projet Apache Camel fournit tous les outils nécessaires permettant de répondre à ce besoin. Nous verrons donc dans cet article comment tester le composant que nous avons développé.

Pour finir, nous verrons comment intégrer notre développement à un projet Camel, ainsi que les limites de notre composant et les solutions pour résoudre ces limitations.

Tester la classe ApnsConsumer

Bien que le projet Apache Camel permette de travailler naturellement avec Spring, nous nous attarderons dans un premier temps sur les méthodes permettant de tester notre composant sur un mode standalone. Pour cela, nous étendrons la classe CamelTestSupport. Cette dernière fournit les méthodes nécessaires pour effectuer différentes assertions sur les valeurs attendues en fin de traitement, en particulier sur des endpoints de type MockEndpoint.

Nous devons tout d’abord implémenter les 2 méthodes suivantes:

Méthode Description
createCamelContext() Permet de créer le contexte Camel qui sera utilisé par le test
createRouteBuilder() Permet de construire la route qui sera appelée par le test. Idéalement, la route doit se terminer par l’appel d’un MockEpoint

Avant l’exécution de nos tests, un serveur APNS bouchonné doit être lancé. Il enverra sur le flux feedback des données factices.

L’objectif de ce test est de valider que le endpoint apns:consumer est capable de consommer les informations de test renvoyées par le flux feedback du serveur bouchonné. Le endpoint Camel mock:result nous permet de valider les assertions nécessaires sur les messages reçus par ce endpoint.

Le endpoint apns:consumer est configuré pour poller le flux feedback de l’APNS toutes les 500ms. Le test ne doit donc pas durer raisonnablement plus de 5 secondes en tenant compte du temps d’attente d’une seconde avant les assertions et du temps de démarrage du serveur bouchon APNS. Un timeout de 5 secondes est donc placé sur le test pour s’assurer que ce dernier tombe en erreur si un événement bloquant inattendu empêche le test de se terminer.

public class ApnsConsumerTest extends CamelTestSupport {

    ApnsServerStub server;

    public ApnsConsumerTest() {
    	super();
    }


    /**
     * Démarrage du serveur bouchonné simulant l'APNS
     */
    @Before
    public void startup() throws InterruptedException {
        server = ApnsServerStub.prepareAndStartServer(FixedCertificates.TEST_GATEWAY_PORT, FixedCertificates.TEST_FEEDBACK_PORT);
    }

    /**
     * Arrêt du serveur bouchonné simulant l'APNS
     */
    @After
    public void stop() {
        server.stop();
    }


    /**
     * Test de l'ApnsConsumer. L'objectif est de vérifier que le flux feedback est
     * bien consommé par la route Camel. Pour cela on vérifie après consommation
     * que le endpoint MockEndpoint a bien reçu les messages.
     */
    @Test(timeout=5000)
    public void testConsumer() throws Exception {

    	byte[] deviceTokenBytes = ApnsUtils.createRandomDeviceTokenBytes();
        String deviceToken = ApnsUtils.encodeHexToken(deviceTokenBytes);

        MockEndpoint mock = getMockEndpoint("mock:result");
        mock.expectedMessageCount(1);
        mock.message(0).body().isInstanceOf(InactiveDevice.class);

        byte[] feedBackBytes = ApnsUtils.generateFeedbackBytes(deviceTokenBytes);
        server.toSend.write(feedBackBytes);

        Thread.sleep(1000);

        assertMockEndpointsSatisfied();

        InactiveDevice inactiveDevice = (InactiveDevice)mock.getExchanges().get(0).getIn().getBody();
        assertNotNull(inactiveDevice);
        assertNotNull(inactiveDevice.getDate());
        assertNotNull(inactiveDevice.getDeviceToken());
        assertEquals(deviceToken, inactiveDevice.getDeviceToken());
    }

    /**
     * Création et configuration du contexte Camel avec une configuration de test.
     * Enregistrement du composant APNS, et configuration de l'ApnsService
     */
    protected CamelContext createCamelContext() throws Exception {
        CamelContext camelContext = super.createCamelContext();

        ApnsServiceFactory apnsServiceFactory = ApnsUtils.createDefaultTestConfiguration();
        ApnsService apnsService = apnsServiceFactory.getApnsService();

        ApnsComponent apnsComponent = new ApnsComponent(apnsService);

        camelContext.addComponent("apns", apnsComponent);

        return camelContext;
    }

    /**
     * Création de la route Camel permettant de simuler la consommation du flux
     * feedback de l'APNS
     */
    protected RouteBuilder createRouteBuilder() throws Exception {
        return new RouteBuilder() {
            public void configure() throws Exception {
                from("apns:consumer?initialDelay=500&delay=500&timeUnit=MILLISECONDS")
                	.to("log:com.apache.camel.component.apns?showAll=true&multiline=true")
                	.to("mock:result");
            }
        };
    }

}

Tester la classe ApnsProducer

De la même façon que nous nous y sommes pris pour la classe de test ApnsConsumerTest, nous allons étendre la classe CamelTestSupport pour tester notre classe ApnsProducer. L’objectif est de vérifier que le serveur bouchonné reçoit bien les messages produits.

public class ApnsProducerTest extends CamelTestSupport {

    private ApnsServerStub server;
    private String FAKE_TOKEN = "19308314834701ACD8313AEBD92AEFDE192120371FE13982392831701318B943";

    public ApnsProducerTest() {
    	super();
    }


    /**
     * Démarrage du serveur bouchonné simulant l'APNS
     */
    @Before
    public void startup() {
        server = ApnsServerStub.prepareAndStartServer(FixedCertificates.TEST_GATEWAY_PORT, FixedCertificates.TEST_FEEDBACK_PORT);
    }

    /**
     * Arrêt du serveur bouchonné simulant l'APNS
     */
    @After
    public void stop() {
        server.stop();
    }


    /**
     * Test de l'ApnsProducer. L'objectif est de vérifier que le serveur APNS a
     * bien reçu les notifications envoyées. 
     * Pour cela on vérifie après production des notifications que le serveur bouchonné
     * a bien reçu les messages: On s'assure que les tableaux de bytes de la notification
     * produite et du contenu reçu par le serveur sont bien égaux.
     */
    @Test(timeout=2000)
    public void testProducer() throws Exception {
    	String message = "Hello World";
    	String messagePayload = APNS.newPayload().alertBody(message).build();

        ApnsNotification apnsNotification = new ApnsNotification(FAKE_TOKEN, messagePayload);
        server.stopAt(apnsNotification.length());

        template.sendBody("direct:test", message);

        server.messages.acquire();
        assertArrayEquals(apnsNotification.marshall(), server.received.toByteArray());
    }

    /**
     * Création et configuration du contexte Camel avec une configuration de test.
     * Enregistrement du composant APNS, et configuration de l'ApnsService
     */
    protected CamelContext createCamelContext() throws Exception {
        CamelContext camelContext = super.createCamelContext();

        ApnsServiceFactory apnsServiceFactory = ApnsUtils.createDefaultTestConfiguration();
        ApnsService apnsService = apnsServiceFactory.getApnsService();

        ApnsComponent apnsComponent = new ApnsComponent(apnsService);
        camelContext.addComponent("apns", apnsComponent);

        return camelContext;
    }

    /**
     * Création de la route Camel permettant de simuler la production de messages
     * à destination des serveurs APNS.
     */
    protected RouteBuilder createRouteBuilder() throws Exception {
        return new RouteBuilder() {
            public void configure() throws Exception {
                from("direct:test").
                setHeader(ApnsConstants.HEADER_TOKENS, constant(FAKE_TOKEN)).
                to("apns:notify");
            }
        };
    }
}

Tester l’intégration d’un composant Camel avec Spring

Les configurations Camel sont souvent déclarées par des fichiers de configuration Spring. C’est pourquoi il est nécessaire de tester l’intégration du composant dans une configuration Spring. Un bon moyen d’automatiser ce type de test est d’utiliser les classes de support JUnit fournies par Spring ( _AbstractJUnit4SpringContextTests_ ).

L’ensemble de la configuration est déclarée dans le fichier SpringApnsConsumerTest-context.xml associé au test unitaire. Les différents beans utilisés par la classe de test sont injectés via les annotations @Autowired et @EndpointInject(uri = « mock:result »).

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:camel="http://camel.apache.org/schema/spring"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

	<bean id="apnsServiceFactory" class="org.apache.camel.component.apns.factory.ApnsServiceFactory">
		<property name="feedbackHost" value="localhost" />
		<property name="feedbackPort" value="7843" />
		<property name="gatewayHost" value="localhost" />
		<property name="gatewayPort" value="7654" />
		<property name="sslContext" ref="sslContext" />
	</bean>

	<!-- Déclaration de l'ApnsService, utilisé par l'ApnsComponent -->
	<bean id="apnsService" factory-bean="apnsServiceFactory" factory-method="getApnsService" />


	<bean id="sslContext" class="org.apache.camel.component.apns.util.FixedCertificates" factory-method="clientContext"/>

	<!-- Déclaration de l'ApnsComponent. Le scheme utilisé pour la création des endpoints -->
	<!-- sera l'Id utilisé pour la création du bean -->
	<bean id="apns" class="org.apache.camel.component.apns.ApnsComponent">
		<property name="apnsService" ref="apnsService" />
	</bean>

	<!-- Déclaration du context Camel, et de la route de test: 'apns-test' -->
	<!-- Les message reçus par le flux feedback seront envoyés vers le endpoint mock:result -->
	<camelcontext id="camel-apns-test" xmlns="http://camel.apache.org/schema/spring">

		<route id="apns-test">
			<from uri="apns:consumer?initialDelay=500&amp;amp;amp;amp;delay=500&amp;amp;amp;amp;timeUnit=MILLISECONDS" />
			<to uri="log:org.apache.camel.component.apns?showAll=true&amp;amp;amp;amp;multiline=true" />
			<to uri="mock:result" />
		</route>

	</camelcontext>

</beans>

Une fois la déclaration du composant Camel effectuée via le fichier de configuration Spring, il ne reste plus qu’à injecter les différents beans configurés dans les propriétés de la classe de test. Le contenu du test en lui-même reste identique, cependant la partie configuration a disparu ce qui rend le test plus lisible. Afin de faire disparaître du test ce qui peut encore en parasiter sa lisibilité, une classe de test parent peut être créée pour contenir les méthodes d’initialisation et d’arrêt du serveur APNS de test.

@ContextConfiguration
public class SpringApnsConsumerTest extends AbstractJUnit4SpringContextTests {

    ApnsServerStub server;

    /**
     * Injection du context Camel déclaré via Spring
     */
    @Autowired
    protected CamelContext camelContext;

    /**
     * Injection du endpoint Camel "mock:result" déclaré via Spring
     */
    @EndpointInject(uri = "mock:result")
    protected MockEndpoint mock;

    public SpringApnsConsumerTest() {
    	super();
    }


    /**
     * Démarrage du serveur bouchonné simulant l'APNS
     */
    @Before
    public void startup() throws InterruptedException {
        server = ApnsServerStub.prepareAndStartServer(FixedCertificates.TEST_GATEWAY_PORT, FixedCertificates.TEST_FEEDBACK_PORT);
    }

    /**
     * Arrêt du serveur bouchonné simulant l'APNS
     */
    @After
    public void stop() {
        server.stop();
    }


    /**
     * Test du consommateur
     */
    @Test(timeout=5000)
    public void testConsumer() throws Exception {

    	byte[] deviceTokenBytes = ApnsUtils.createRandomDeviceTokenBytes();
        String deviceToken = ApnsUtils.encodeHexToken(deviceTokenBytes);

        mock.expectedMessageCount(1);
        mock.message(0).body().isInstanceOf(InactiveDevice.class);

        byte[] feedBackBytes = ApnsUtils.generateFeedbackBytes(deviceTokenBytes);
        server.toSend.write(feedBackBytes);

        Thread.sleep(1000);

        mock.assertIsSatisfied();

        InactiveDevice inactiveDevice = (InactiveDevice)mock.getExchanges().get(0).getIn().getBody();
        Assert.assertNotNull(inactiveDevice);
        Assert.assertNotNull(inactiveDevice.getDate());
        Assert.assertNotNull(inactiveDevice.getDeviceToken());
        Assert.assertEquals(deviceToken, inactiveDevice.getDeviceToken());
    }

}

Limitations du composant actuel

Dans un soucis de simplicité, le composant présenté est limité fonctionnellement et ne propose qu’un ensemble réduit de fonctionnalités. L’implémentation proposée dans ce billet nous oblige à déclarer les tokens à notifier directement au niveau de la route. Cependant une implémentation plus complète pourrait permettre d’exploiter des en-têtes par exemple pour rendre la sélection de token à notifier plus dynamique.

Heureusement, la richesse du framework Apache Camel nous permet de passer outre cette restriction et d’obtenir l’aspect dynamique souhaité en utilisant le pattern Recipient List, qui permet par exemple l’extraction des URIs de destination depuis un en-tête de message produit au préalable.

RouteBuilder builder = new RouteBuilder() {
    public void configure() {
        from("direct:a").recipientList(
        header("recipientListHeader").tokenize(","));
    }
};

Dans cet exemple, l’en-tête recipientListHeader contiendrait les différentes URIs séparées par des virgules.

Utilisation du composant dans un projet

Pour utiliser le composant développé dans cette série d’articles, il suffit de l’importer en tant que dépendance Maven dans votre projet Camel et de déclarer le repository qui permettra de le charger à défaut de l’avoir déjà dans son répository local.

Ce qui donne les lignes suivantes à ajouter dans le pom de votre projet:

  • La dépendance Maven:
<dependency>
    <groupid>org.apache.camel</groupid>
    <artifactid>camel-apns</artifactid>
    <version>2.4.0</version>
</dependency>
  • Le repository Maven qui met à disposition le composant:
<repositories>
    <repository>
        <id>camel-apns.repo-release</id>
        <url>http://camel-apns.googlecode.com/svn/maven/public/repository/release/</url>
    </repository>
</repositories>

Exemples d’utilisation

Afin de donner une vision synthétique de l’usage de notre composant, voici un condensé de quelques exemples d’utilisation:

  • Consommation du flux feedback avec une configuration Java
      from("apns:consumer")
            .to("log:com.apache.camel.component.apns?showAll=true&amp;amp;amp;amp;multiline=true")
            .to("mock:result");

  • Envoi de notifications avec une configuration Java
      from("direct:test")
            .setHeader(ApnsConstants.HEADER_TOKENS, constant(FAKE_TOKEN))
            to("apns:notify");
  • Envoi d’une notification avec une configuration Spring
<camelcontext id="camel-apns-test" xmlns="http://camel.apache.org/schema/spring">

	<route id="apns-test">
		<from uri="apns:consumer" />
		<to uri="log:org.apache.camel.component.apns?showAll=true&amp;amp;amp;amp;multiline=true" />
		<to uri="mock:result" />
	</route>

</camelcontext>

Conclusion

Nous avons vu dans cet article comment créer et tester un composant Camel qui communique avec les serveurs de notifications d’Apple. Les API du framework ont été pensées pour faciliter le développement de nouveaux composants, et vous permettront d’implémenter et de tester facilement les composants dont vous avez besoin. Vous pouvez consulter le code source du composant camel-apns présenté dans cette série d’article sur sa page Google Code.

Liens utiles

Le site google code du composant ‘camel-apns’ présenté dans le billet:

D’autres liens utiles sur le développement de composants Camel:

Précédentes parties de l’article:

Créer un composant Apache Camel de connexion à l’APNS – 2 sur 3

Nous avons vu dans un premier article comment initier le développement d’un composant Apache Camel. Cependant, nous n’avons pas encore abordé son développement à proprement parler et notre composant ne permet pas encore de communiquer avec les serveurs Apple. Nous allons donc voir dans cet article comment implémenter les différentes classes nécessaires au bon fonctionnement de notre composant.
Pour rappel l’objectif est de développer un composant capable de communiquer avec l’Apple Push Notification Service, qui permet d’envoyer des notifications aux appareils mobiles d’Apple (iPad, iPhone, iPod Touch).

Implémentation du composant

Les quatre interfaces suivantes doivent être implémentées pour développer un composant Camel permettant à la fois de consommer et produire des messages :

Interfaces à implémenter Description
Component Elle permet de créer les endpoints relatifs au composant.
Endpoint Elle permet la création des Consumers et Producers relatifs à un endpoint défini par son URI.
Consumer Elle correspond à l’implémentation des Consumers relatifs au endpoint d’un composant.
Producer Elle correspond à l’implémentation des Producers relatifs au endpoint d’un composant.

La classe ApnsService

Pour communiquer avec les serveurs Apple, nous devons instancier un objet ApnsService. Le framework java-apns fournit pour cela la classe ApnsServiceBuilder. Celle-ci permet de construire facilement un objet ApnsService en spécifiant au builder la configuration souhaitée.

Le builder est obtenu grâce à la classe utilitaire Apns :

        ApnsServiceBuilder builder = APNS.newService();

Ensuite, il suffit d’appeler les différentes méthodes de configuration du builder. Dans le cadre d’une configuration simple, le code source suivant est suffisant :


// Instanciation du builder
ApnsServiceBuilder builder = APNS.newService();


InputStream certificateInputStream = null;
try {
    // Ouverture de l'inputStream correspondant au certificat
	certificateInputStream = ResourceUtils.getInputStream("certificate_classpath");
	// Configuration du certificat utilisé pour communiquer avec l'APNS
	builder.withCert(certificateInputStream, "certificate_password")

}
finally {
	// Fermeture de l'inputStream correspondant au certificat
	ResourceUtils.close(certificateInputStream);
}

// Configuration des URLs par défaut de production vers les serveurs APNS
builder.withProductionDestination();

// Configuration d'un pool de 5 connexions vers les serveurs APNS
builder.asPool(5);
// Configuration d'une politique de reconnexion uniquement lorsqu'une connexion vers les serveurs APNS est fermée
builder.withReconnectPolicy(ReconnectPolicy.Provided.NEVER);

// Obtention de l'instance configurée
ApnsService apnsService = builder.build();

Note : Un exemple de configuration plus complet de l’objet ApnsService peut être trouvé dans le code source du composant camel-apns.

Afin d’intégrer de façon efficace notre composant avec Spring, la classe ApnsServiceBuilder se révèle être un bon point de départ pour écrire une factory permettant de construire un objet ApnsService via Spring.

La classe ApnsComponent

L’interface Component correspond à l’unité de base définissant un composant Camel. Ce dernier est associé au scheme d’une URI lorsqu’il est ajouté à un contexte d’exécution.

Une implémentation par défaut de cette interface est fournie (en l’occurrence la classe DefaultComponent), ce qui permet de se focaliser sur les fonctionnalités de notre composant. Un composant Camel a pour rôle de créer les endpoints relatifs aux URI définies dans le contexte Camel. Cette tâche est réalisée par la méthode createEndpoint.

Chaque endpoint créé à partir d’un même ApnsComponent utilisera une unique configuration pour échanger avec l’APNS. C’est pourquoi le service d’accès à l’APNS sera injecté au niveau de l’objet ApnsComponent.

Le bean apnsService peut être setté par le constructeur ou bien par un setter pour faciliter l’intégration avec Spring.

public class ApnsComponent extends DefaultComponent {

	private ApnsService apnsService;

	public ApnsComponent() {
		super();
	}

	public ApnsComponent(ApnsService apnsService) {
		super();
		AssertUtils.notNull(apnsService, "apnsService is mandatory");
		this.apnsService = apnsService;
	}

	public ApnsComponent(CamelContext context) {
		super(context);
	}

	public ApnsService getApnsService() {
		return apnsService;
	}

	@SuppressWarnings("unchecked")
	protected Endpoint createEndpoint(String uri, String remaining, Map parameters) throws Exception {
		ApnsEndpoint endpoint = new ApnsEndpoint(uri, this);
		setProperties(endpoint, parameters);

		return endpoint;
	}

    /**
     * L'objet ApnsService peut être setté s'il le constructeur vide a été appelé pour instancier un objet ApnsComponent
     */
	public void setApnsService(ApnsService apnsService) {
		if (this.apnsService != null) {
			throw new IllegalArgumentException("apnsService already setted");
		}
		this.apnsService = apnsService;
	}

}

La classe ApnsEndpoint

La classe ApnsEndpoint permet de gérer la création des objets Consumer et Producer définis dans les routes Camel. Dans notre cas, nous étendrons la classe ScheduledPollEndpoint. Elle permet d’implémenter un endpoint qui saura créer des objets Consumer dont le déclenchement s’effectuera à intervalle régulier.

C’est la classe ApnsEndpoint qui a la charge de gérer les paramètres passés dans l’URI. Ainsi, les tokens passés en paramètres de l’URI d’un objet ApnsEndpoint seront injectés automatiquement via les setters correspondant aux paramètres renseignés. Si un paramètre ne correspond pas à un setter déclaré, une exception sera alors lancée.

Afin de faciliter le travail de configuration des paramètres spécifiques au travail de polling, c’est la classe ScheduledPollEndpoint qui lira automatiquement les paramètres de déclenchement renseignés sur l’URI. L’ApnsEndpoint aura donc toutes les informations nécessaires pour configurer les paramètres de déclenchement de l’objet ApnsConsumer.

Dans le cas de l’exécution de tests, les paramètres de déclenchement de la consommation de messages en provenance de l’APNS peuvent être configurés de la façon suivante :

from("apns:consumer?initialDelay=500&amp;amp;amp;amp;amp;amp;delay=500&amp;amp;amp;amp;amp;amp;timeUnit=MILLISECONDS")
	.to("log:com.apache.camel.component.apns?showAll=true&amp;amp;amp;amp;amp;amp;multiline=true")
	.to("mock:result");

L’implémentation de la classe ApnsEndpoint donnera le code source suivant :

public class ApnsEndpoint extends ScheduledPollEndpoint {

	@SuppressWarnings("unused")
	private static final Log LOG = LogFactory.getLog(ApnsEndpoint.class);

	private CopyOnWriteArraySet<defaultconsumer> consumers = new CopyOnWriteArraySet</defaultconsumer><defaultconsumer>();

	private String tokens;

	public ApnsEndpoint(String uri, ApnsComponent component) {
		super(uri, component);
	}

	public String getTokens() {
		return tokens;
	}

	public void setTokens(String tokens) {
		this.tokens = tokens;
	}

	private ApnsComponent getApnsComponent() {
		return (ApnsComponent)getComponent();
	}

    /**
     * On obtient une instance de la clase ApnsService depuis l'object ApnsComponant
     */
	public ApnsService getApnsService() {
		return getApnsComponent().getApnsService();
	}

    /**
      * Indique que le endpoint n'est instancié qu'une seule fois par le contexte Camel
      */
	public boolean isSingleton() {
		return true;
	}

    /**
     * Permet d'obtenir la liste des consumers
     */
	protected Set</defaultconsumer><defaultconsumer> getConsumers() {
		return consumers;
	}

    /**
     * Permet de créer un consumer
     */
	public Consumer createConsumer(Processor processor) throws Exception {

		ApnsConsumer apnsConsumer = new ApnsConsumer(this, processor);
		configureConsumer(apnsConsumer);

		return apnsConsumer;
	}

	/**
	 * Permet de créer un producer
	 */
	public Producer createProducer() throws Exception {
		return new ApnsProducer(this);
	}

}

La classe ApnsProducer

La classe ApnsProducer correspond à la classe qui permet d’envoyer des notifications aux terminaux mobiles Apple via les serveurs APNS. La classe abstraite DefaultProducer sera étendue pour fournir un support de base à notre implémentation.

Seule la méthode process reste ainsi à renseigner. Il suffira d’y implémenter l’envoi de la notification à l’APNS comme suit :

public class ApnsProducer extends DefaultProducer {

	private static final transient Log LOG = LogFactory.getLog(ApnsProducer.class);

	private ApnsEndpoint endpoint;

	private List<string> tokenList;

	public ApnsProducer(ApnsEndpoint endpoint) {
		super(endpoint);
		this.endpoint = endpoint;
		configureTokens(apnsEndpoint);
	}

    /**
     * La méthode configureTokens permet d'extraire la liste de tokens destinataires
     * des notifications envoyées au endpoint.
     */
	private void configureTokens(ApnsEndpoint apnsEndpoint) {
		if (StringUtils.isNotEmpty(apnsEndpoint.getTokens())) {
			try {
				this.tokenList = extractTokensFromString(apnsEndpoint.getTokens());
			} catch (CamelException e) {
				throw new IllegalArgumentException(e);
			}
		}
	}

	/**
	 * Méthode appelée par le producer pour traiter les échanges Camel
	 */
	public void process(Exchange exchange) throws Exception {
		notify(exchange);
	}

	/**
	 * La méthode notify  est appelée pour envoyer une notification aux serveurs APNS.
	 */
	private void notify(Exchange exchange) throws ApnsException, CamelException {

		String payload = exchange.getIn().getBody(String.class);

                // Une copie de la liste des tokens à notifier est passée en paramètre,
                // ainsi que le payload du message
		endpoint.getApnsService().push(new ArrayList</string><string>(tokenList), payload);
	}

	/**
	 * On extrait une liste de tokens à partir d'une chaîne de caractères contenant
	 * des tokens séparés par un point virgule.
	 */
	private List</string><string> extractTokensFromString(String tokensStr) throws CamelException {

		tokensStr = StringUtils.trim(tokensStr);

		if (tokensStr.isEmpty()) {
			throw new CamelException("No token specified");
		}

		String[] tokenArray = tokensStr.split(";");

		int tokenArrayLength = tokenArray.length;
		for (String token : tokenArray) {
			token = token.trim();
			int tokenLength = token.length();
			// La taille d'un token est limitée à 64 caractères
			if (tokenLength != 64) {
				throw new CamelException("Token has wrong size['" + tokenLength + "']: " + token);
			}
		}

		List</string><string> tokens = Arrays.asList(tokenArray);

		return tokens;
	}

}

Une fois la notification envoyée aux serveurs APNS, le terminal Apple recevra la notification et l’affichera comme suit dans le cas d’une notification texte:

iphone-push-notification-1st-screenshots  iphone-push-notification-2nd-screenshots

La classe ApnsConsumer

La classe ApnsConsumer a pour objectif de nous permettre de consommer le flux feedback renvoyé par les serveurs APNS, permettant de connaître les terminaux mobiles Apple pour lesquels il n’est plus nécessaire d’envoyer de notifications (Par exemple, lorsque l’application a été désintallée).

Comme vu précédemment, la stratégie de consommation peut suivre un pattern de type Event Driven Consumer ou bien de Polling Consumer. Ici, nous choisirons le pattern Event Driven Consumer, et nous étendrons la classe ScheduledPollConsumer qui prend en charge toute la complexité de la gestion de consommation des messages et nous laisse nous concentrer sur le principal, c’est à dire, fournir les messages à consommer par nos routes de traitement.

Pour cela, il suffit d’implémenter la méthode poll qui permet d’interroger le service feedback d’Apple selon le timing d’interrogation configuré par l’URI.

C’est la classe ApnsEndpoint, qui au moment de la création de l’objet ApnsConsumer, configurera les paramétrages de polling à partir des paramètres récupérés via l’URI.

La méthode doStart aura pour rôle de vérifier qu’un seul objet ApnsConsumer est créé afin de ne pas consommer en double les messages d’une même configuration de l’ApnsService.

La méthode poll est implémentée ici de façon à récupérer les informations sur les appareils pour lesquels il ne faut plus envoyer de notifications. Pour cela, nous récupérons dans un premier temps une liste d’objets InactiveDevice renvoyés par le flux feedback de l’APNS, puis nous itérons sur cette liste pour les faire traiter par le processor Camel, qui n’est autre que la route de traitement Camel.

public class ApnsConsumer extends ScheduledPollConsumer {

	private static final int DEFAULT_CONSUME_INITIAL_DELAY = 10;
	private static final int DEFAULT_CONSUME_DELAY = 3600;
	private static final TimeUnit DEFAULT_CONSUME_TIME_UNIT = TimeUnit.SECONDS;
	private static final boolean DEFAULT_APNS_FIXED_DELAY = true;

	/**
	 * La configuration par défaut des paramètres de polling est fait lors de la
	 * construction de l'objet. Ces valeurs pourront être écrasées par des valeurs
	 * configurées via l'URI.
	 */
	public ApnsConsumer(ApnsEndpoint apnsEndpoint, Processor processor) {
		super(apnsEndpoint, processor);

		setInitialDelay(DEFAULT_CONSUME_INITIAL_DELAY);
		setDelay(DEFAULT_CONSUME_DELAY);
		setTimeUnit(DEFAULT_CONSUME_TIME_UNIT);
		setUseFixedDelay(DEFAULT_APNS_FIXED_DELAY);
	}

	/**
	 * Chaque élément de cette liste obtenue par l'appel de la méthode  getInactiveDevices()
	 * sera passé à la route Camel via l'appel de la méthode getProcessor().process(e)
	 */
	protected void poll() throws Exception {
		List<inactivedevice> inactiveDeviceList = getInactiveDevices();

		Iterator</inactivedevice><inactivedevice> it = inactiveDeviceList.iterator();

		while(it.hasNext()) {
			InactiveDevice inactiveDevice = it.next();

			Exchange e = getEndpoint().createExchange();

			e.getIn().setBody(inactiveDevice);

			// On donne chaque élément de la liste de terminaux inactifs
			// reçu sur le flux feedback pour qu'il soit traité par la route associée
			getProcessor().process(e);
		}
	}

	/**
	 * La méthode getInactiveDevices() permet d'obtenir depuis le flux feedback une
	 * liste d'objects InactiveDevice.
	 */
	private List</inactivedevice><inactivedevice> getInactiveDevices() {
		ApnsEndpoint ae = (ApnsEndpoint)getEndpoint();

		Map<string , Date> inactiveDeviceMap = ae.getApnsService().getInactiveDevices();

		List<inactivedevice> inactiveDeviceList = new ArrayList</inactivedevice><inactivedevice>();

		for (Entry<string , Date> inactiveDeviceEntry : inactiveDeviceMap.entrySet()) {
			String deviceToken = inactiveDeviceEntry.getKey();
			Date date = inactiveDeviceEntry.getValue();

			InactiveDevice inactiveDevice = new InactiveDevice(deviceToken, date);

			inactiveDeviceList.add(inactiveDevice);
		}

		return inactiveDeviceList;
	}

    @Override
	public ApnsEndpoint getEndpoint() {
		return (ApnsEndpoint)super.getEndpoint();
	}

	/**
	 * La consommation du flux feedback doit être faite par un unique consumer.
	 * La méthode doStart permet de s'enassurer lors du démarrage.
	 * Si un autre consumer est déjà déclaré, alors une exception sera lancée.
	 */
    @Override
    protected void doStart() throws Exception {
        // only add as consumer if not already registered
        if (!getEndpoint().getConsumers().contains(this)) {
            if (!getEndpoint().getConsumers().isEmpty()) {
                throw new IllegalStateException("Endpoint " + getEndpoint().getEndpointUri() + " only allows 1 active consumer but you attempted to start a 2nd consumer.");
            }
            getEndpoint().getConsumers().add(this);
        }
        super.doStart();
    }

    @Override
    protected void doStop() throws Exception {
        super.doStop();
        getEndpoint().getConsumers().remove(this);
    }

}

Conclusion

Nous avons vu dans cette seconde partie comment implémenter les classes nécessaires au développement d’un composant Apache Camel. Nous verrons dans une dernière partie comment mettre en place différentes stratégies de test pour valider notre composant.

Liens utiles

Le site google code du composant ‘camel-apns’ présenté dans l’article :

Parties suivantes et précédentes de l’article :

Créer un composant Apache Camel de connexion à l’APNS – 1 sur 3

Le projet Apache Camel est un framework d’intégration basé sur l’implémentation de patterns d’intégration d’entreprise connus. Il permet d’implémenter des règles de routage et de médiation à partir d’un DSL Java ou bien via des configurations Spring au format Xml.

Apache Camel utilise la notion d’URIs, ce qui permet de travailler facilement avec différents types de transport ou modèles d’échange de messages, tels que HTTP ou JMS. De la même manière, Apache Camel est capable de travailler avec différents formats de données (Csv, Xml, Json, …).

L’utilisation des composants fournis out of the box permet de travailler avec de nombreux protocoles et formats de données. Mais qu’en est-il lorsqu’un connecteur vient à manquer?

Pour répondre à cette question, le projet Apache Camel propose une API complète permettant d’implémenter soi-même des composants adaptés à son besoin.

Objectif

L’objectif de cet article est de présenter comment initier le développement d’un composant Apache Camel.

Pour cela, nous mettrons en place un composant qui permettra de dialoguer avec l’Apple Push Notification Service, dans le but d’envoyer des notifications aux différents terminaux Apple (iPad, iPhone, iPod Touch).

Le composant proposera à la fois des endpoints de type producer et de type consumer qui permettrons respectivement d’envoyer des notifications et de lire un flux feedback d’informations en provenance de l’APNS.

En pré-requis, un ensemble de endpoints devra utiliser une même instance de la classe ApnsService proposée par la librairie java-apns, qui permet de dialoguer avec les serveurs d’Apple. La firme à la pomme préconise en effet de créer un nombre limité de connexions, et de réutiliser celles-ci pour communiquer avec ses serveurs. Nous utiliserons également le scheme apns pour déclarer les URIs qui représenteront les endpoints gérés par nos composants.

Présentation de l’APNS

L’Apple Push Notification Service est disponible pour les applications destinées aux iPhones, iPads et iPods touch. Il permet d’envoyer des notifications de 3 types : texte, image ou badge.

Pour envoyer une notification, un provider (une application serveur en général) doit en premier lieu envoyer la notification à l’Apple Push Notification Service qui se chargera à son tour de diffuser la notification aux terminaux concernés.

La notification est composée d’un token (identifiant unique d’un terminal) et d’un payload (contenu de la notification).

Simple notification APNS

L’APNS propose en complément de l’envoi de notifications, un flux feedback d’informations permettant de connaître la liste des terminaux pour lesquels les notifications n’ont pu être délivrées de façon répétée (généralement, lorsque l’application a été désinstallée).

Pour qu’une application iOS soit en mesure de recevoir des notifications, elle doit en premier lieu fournir à l’application serveur (celle qui va envoyer les notifications aux serveurs Apple) toutes les informations nécessaires notamment son token d’identification. Cette opération peut-être réalisée, par exemple, via un simple appel HTTP.

Séquence d'enregistrement

API Java de connexion à l’APNS

De nombreux projets permettent de dialoguer avec l’APNS via différents langages de programmation. Actuellement deux implémentations existent pour le langage Java :

Historiquement, l’implémentation javapns est apparue avant java-apns. Cependant nous choisirons d’utiliser la librairie java-apns qui a pour avantage de proposer des fonctionnalités complètes (gestion de pool de connections, capacité de reconnexion, …), ainsi qu’une API élégante et différentes facilités d’écriture de tests unitaires.

Les endpoints Camel

Le développement d’un endpoint Apache Camel implique d’implémenter une ou plusieurs des méthodes suivantes selon les besoins :

Méthode Description
createProducer() Crée un producer permettant d’envoyer des messages à un endpoint
createConsumer() Crée un consumer implémentant le pattern Event Driven Consumer pour consommer des messages depuis un endpoint
createPollingConsumer() Crée un consumer implémentant le Polling Consumer pour consommer des messages depuis un endpoint

L’Event Driven Consumer

Solution Event Driven Consumer

Ce modèle événementiel de consommation des messages correspond au modèle de consommation par défaut des messages par les composants Camel.

Le Polling Consumer

Solution Polling Consumer

Le polling consumer propose 3 types de méthodes:

Méthode Description
receive() Attend qu’un échange soit disponible et le retourne (potentiellement indéfiniment)
receive(long) Attend un échange jusqu’à un timeout défini. Renvoie null si aucun échange n’a été reçu dans le temps imparti
receiveNoWait() Tente de recevoir un message sans attendre de timeout et retourne null si aucun échange n’est disponible

Le composant camel-jms implémente cette notion de polling-consumer pour s’intégrer à Apache Camel.

Générer un squelette de composant

Partir de zéro pourrait s’avérer compliqué. Heureusement le projet Apache Camel met à disposition l’archétype Maven: camel-artefact-component.

mvn archetype:create 
-DarchetypeGroupId=org.apache.camel.archetypes 
-DarchetypeArtifactId=camel-archetype-component 
-DarchetypeVersion=2.3.0 
-DgroupId=org.apache.camel 
-DartifactId=camel-apns

L’exécution de la ligne de commande précédente va créer un squelette de composant sur lequel s’appuyer pour développer notre composant:

Contenu généré par l'archetype Maven

L’archétype va générer la structure nécessaire au développement du composant Camel. Cependant il sera nécessaire de personnaliser le composant nouvellement créé pour qu’il corresponde au nommage souhaité :

  • ‘META-INF/services/<PACKAGE_NAME>/direct’ devra être renommé selon le scheme de l’URI de votre composant (Dans notre cas: ‘apns’).
  • Les différentes classes générées seront préfixées par ‘Apns’ plutôt que ‘Direct’.

Il est possible de supprimer les classes qui ne seraient pas utilisées dans le cas où le composant serait uniquement destiné à consommer des messages ou à bien en produire.

Après renommage, le résultat obtenu doit être le suivant :

Vue composant APNS

Importer son composant dans Eclipse

Pour cela, il est possible d’utiliser la commande suivante:

mvn eclipse:eclipse

Le fichier pom.xml

Il est conseillé d’hériter du projet parent camel-parent:

	 
<parent>
	<groupid>org.apache.camel</groupid>
	<artifactid>camel-parent</artifactid>
	<version>2.4.0</version>
</parent>

Le composant camel-parent déclare en effet un certain nombre de dépendances nécessaires, ainsi que le type bundle qui permet d’OSGifier son composant. Ceci permettra aux utilisateurs/développeurs de le déployer dans un conteneur OSGi, tel qu’Apache ServiceMix.

Nous allons donc déclarer le packaging du composant, ainsi que différentes propriétés nécessaires à l’OSGificiation comme suit:

<packaging>bundle</packaging>
...
<properties>
	...
	<!-- OSGi bundles properties -->
	<camel .osgi.import.pkg><strong></strong></camel>
	<camel .osgi.private.pkg>!</camel>
	<camel .osgi.export>${camel.osgi.export.pkg}*;version=${camel.osgi.export.version}</camel>
	<camel .osgi.export.version>${project.version}</camel>
	<camel .osgi.import>${camel.osgi.import.pkg}</camel>
	<camel .osgi.symbolic.name>${groupId}.${artifactId}</camel>
</properties>

Il est également nécessaire de déclarer les dépendances Camel dont nous aurons besoin, ainsi que les dépendances nécessaires à l’implémentation du composant:

<dependencies>
	...
	<!-- Camel -->
	<dependency>
		<groupid>org.apache.camel</groupid>
		<artifactid>camel-core</artifactid>
		<version>${camel.version}</version>
	</dependency>
	<dependency>
		<groupid>org.apache.camel</groupid>
		<artifactid>camel-spring</artifactid>
		<version>${camel.version}</version>
	</dependency>
	<dependency>
		<groupid>org.apache.camel</groupid>
		<artifactid>camel-test</artifactid>
		<version>${camel.version}</version>
		<scope>test</scope>
	</dependency>
	...
	<!-- APNS -->
	<dependency>
		<groupid>com.notnoop.apns</groupid>
		<artifactid>apns</artifactid>
		<version>${apns.version}</version>
		<exclusions>
			<exclusion>
				<groupid>ch.qos.logback</groupid>
				<artifactid>logback-classic</artifactid>
			</exclusion>
		</exclusions>
	</dependency>
	...
</dependencies>

Le projet Apache Camel propose le plugin camel-maven-plugin que nous allons déclarer dans notre fichier pom. Celui-ci permet de lancer en ligne de commande une instance standalone d’Apache Camel et de démarrer les routes qui sont déclarées dans le classpath via le fichier Spring suivant: ‘META-INF/spring/camel-context.xml’.

<!-- allows the route to be ran via 'mvn camel:run' -->
<plugin>
	<groupid>org.apache.camel</groupid>
	<artifactid>camel-maven-plugin</artifactid>
	<version>${camel.version}</version>
</plugin>

Plusieurs entrées repository et pluginReposity doivent également être déclarées dans le pom afin que Maven puisse importer différentes dépendances et plugins nécessaires à notre projet:

<repositories>
	<repository>
		<id>open.iona.m2</id>
		<name>IONA Open Source Community Release Repository</name>
		<url>http://repo.open.iona.com/maven2</url>
		<snapshots>
			<enabled>false</enabled>
		</snapshots>
		<releases>
			<enabled>true</enabled>
		</releases>
	</repository>
	<repository>
		<id>open.iona.m2-snapshot</id>
		<name>IONA Open Source Community Snapshot Repository</name>
		<url>http://repo.open.iona.com/maven2-snapshot</url>
		<snapshots>
			<enabled>true</enabled>
		</snapshots>
		<releases>
			<enabled>false</enabled>
		</releases>
	</repository>
	<!-- java-apns repository -->
	<repository>
		<id>notnoop-repos</id>
		<url>http://notnoop.github.com/m2-repo</url>
	</repository>
	...
	<repository>
		<id>apache.incubating.releases</id>
		<name>Apache Incubating Release Distribution Repository</name>
		<url>http://people.apache.org/repo/m2-incubating-repository</url>
	</repository>
</repositories>
<pluginrepositories>
	<pluginrepository>
		<id>open.iona.m2</id>
		<name>IONA Open Source Community Release Repository</name>
		<url>http://repo.open.iona.com/maven2</url>
		<snapshots>
			<enabled>false</enabled>
		</snapshots>
		<releases>
			<enabled>true</enabled>
		</releases>
	</pluginrepository>
	<pluginrepository>
		<id>open.iona.m2-snapshot</id>
		<name>IONA Open Source Community Snapshot Repository</name>
		<url>http://repo.open.iona.com/maven2-snapshot</url>
		<snapshots>
			<enabled>true</enabled>
		</snapshots>
		<releases>
			<enabled>false</enabled>
		</releases>
	</pluginrepository>
	<pluginrepository>
		<id>maven-repository.dev.java.net</id>
		<name>Java.net Maven 2 Repository</name>
		<url>http://download.java.net/maven/2</url>
	</pluginrepository>
</pluginrepositories>

Conclusion

Nous avons vu dans cette première partie comment initier le développement d’un composant Camel à l’aide de l’archétype Maven camel-archetype-component.

Dans un prochain billet, nous verrons comment implémenter les différentes classes d’un composant Camel. Puis nous conclurons dans une troisième et dernière partie, par la présentation des méthodes permettant de tester notre composant afin de fournir un livrable de qualité.

Liens utiles

Le site google code du composant camel-apns présenté dans cet article:

Parties suivantes de l’article :

Annonce de jQuery Mobile

C’est une nouvelle qui va à coup sûr faire du bruit dans un monde du développement mobile déjà bouillonnant, puisque vient d’être annoncé la sortie pour la fin d’année de la version mobile du framework jQuery. Et ce n’est pas par la petite porte que le célèbre framework web souhaite faire son entrée, puisque contrairement à ses concurrents déclarés, tels que jQTouch ou bien Sensha Touch, jQuery Mobile se veut être un framework JavaScript ciblant la quasi totalité des plateformes mobiles web actuelles (Blackberry, Windows Mobile, iOS pour l’iPhone et l’iPad, Android, Symbian, Bada, …), et permettant de développer des interfaces riches capable de faire rougir bon nombre d’interfaces graphiques natives.

Avec l’avènement d’HTML5 (WebStorage, WebSockets, Vidéo, Géolocalisation, …), du CSS3, et des navigateurs mobiles dernière génération proposant des moteurs JavaScripts performants, la guerre du web semble se trouver un nouveau terrain de bataille, et le web mobile aura sous peu toutes les armes nécessaires pour déstabiliser le business modèle à peine naissant des Markets, tel que l’iTunes App Store ou bien l’Android Market. Le marché des applications mobiles semble donc suivre la voie de son grand frère, celui des applications PC, et proposera à terme de nombreuses applications riches directement par le web.

L’annonce parue sur le site de jQuery Mobile indique que l’un des objectifs du framework est de pouvoir développer une application unique pour toutes les plateformes mobiles. Ce concept, ne semble pas nouveau puisque l’objectif de J2ME sorti il y a 10 ans déjà était bien de développer une solution unique, rappelez-vous: « Write Once, Run everywhere ». Le slogan semble être ici: « Write Less, Do More », mais l’objectif de fond est bien le même.

Palm avec sa plateforme WebOS et Firefox sont déjà sponsors du projet. Bien qu’encore en développement intense, la sortie première sortie du framework est prévue pour fin 2010. Ce framework pourrait bien être une des grandes surprises mobile de 2010, et pourrait devenir très rapidement un framework incontournable pour le développement web d’applications riches pour mobile.

Pour en apprendre plus sur cette déclinaison de jQuery, vous pouvez suivre les liens suivants et apprécier les designs présentés :

Nokia donne son « Mobile Runtime » Java à la fondation Symbian

Dans un effort de rendre plus accessible le développement d’applications Java pour les mobiles basés sur le système Symbian, Nokia vient d’annoncer, ce 15 juillet, faire don de son « Mobile Runtime for Java Applications » (JRT) à la fondation Symbian (créée en 2008 justement, suite au rachat de Symbian Software Limited). Avec le don de son JRT, Nokia met à disposition pas moins d’1 million de lignes de code Java et C++ correspondant à son runtime, son installeur d’applications, ses API, ses tests et sa documentation. Le tout est mis à disposition sous license Eclipse Public License (EPL) et est disponible comme partie intégrante du dernier Symbian^3 Product Development Kit.

Nokia annonce le support de la norme MIDP 2.1 (JSR 118), ainsi que ses nombreuses JSR, dont l’architecture MSA. Rien de bien nouveau, jusqu’à présent, serait-on tenté de dire. D’un point de vue graphique, la plateforme Symbian^3 apporte le support d’une accélération graphique hardware, ainsi que deux API non standard : la classique Nokia UI API en version 1.4, ainsi qu’une implémentation de l’Eclipse Standard Widget Toolkit (eSWT) UI API en version 1.0.3.

En complément du langage Java, Le SDK Symbian^3 propose d’écrire des applications natives en utilisant le langage C++ et le support intégré du tookit graphique Qt. Un support de développement basé sur les technologies Web est également disponible, il permet de construire des applications basées sur le Nokia’s Web Runtime (WRT) et l’outil PhoneGap, qui permet de tirer parti de différentes fonctionnalités de l’OS habituellement uniquement accessibles par les applications natives.

Pour les curieux, une roadmap de développement permet de se faire une idée des fonctionnalités des futures version de la plateforme.

Quelle stratégie pour Nokia en 2010?

Dès 2008, Nokia avait préparé sa riposte face à la sortie de la plateforme mobile Android de Google. Déjà détenteur de 48% de la société Symbian Limited, Nokia avait racheté les parts restantes auprès de ses concurrents de l’époque (Sony-Ericsson, Panasonic, Siemens, …) pour un montant avoisinant les 250 millions d’euros. Ce rachat qui n’était qu’une première étape devait permettre à Nokia de faire don de la plateforme Symbian à la fondation éponyme. A la même époque les autres acteurs de l’environnement Symbian devaient faire de même: Sony-Ericsson et Motorola devaient, entre autre, fournir la technologie UIQ à la fondation. Le but de la fondation Symbian était donc de créer, à partir de ces différentes contributions, une plateforme mobile unifiée ouverte à la communauté.

En l’espace de 2 ans, le paysage des plateformes mobiles a cependant bien changé: l’iOS d’Apple est devenu un leader emblématique, même s’il commence à se faire disputer le titre par la plateforme Android de Google, tandis que Microsoft tente revenir dans la course pour les fêtes avec son nouveau système Windows Phone 7. En parallèle, de nouveaux acteurs apparaissent tel que Samsung avec son système Bada, ou bien Baidu qui souhaite copier son grand frère Google avec un OS ayant pour ambition de conquérir le marché chinois …

Près de 2 ans après la présentation de sa stratégie pour contrer Google, l’annonce de la sortie de Symbian^3 par Nokia ressemble beaucoup à une tentative désespérée de raccrocher le wagon. Il n’est plus l’heure pour Nokia de redorer son blason, mais plutôt de sauver les meubles dans un secteur où les évolutions se font à coup de bottes de sept lieues. Pour s’en convaincre, il suffit de comparer le paysage des plateformes mobiles aujourd’hui avec celui de 2008. Malgré un parc important d’appareils mobiles déployés et sa communauté de développeurs, Nokia est toujours à la recherche d’un business model efficace. Ses talents d’innovation et de fabricant de combinés haut de gammes semblent être un lointain souvenir tant la concurrence a pris de l’avance rapidement. La stratégie de Nokia est-elle la bonne ? Nokia est en tout cas, aujourd’hui, au pied du mur. Le géant scandinave annonce pour la fin d’année des appareils compatibles avec la toute dernière version de la plateforme Symbian, comme le Nokia N8. Dans un marché en mouvement permanent, cela sera-t-il suffisant ?

Sortie d’Apache Camel 2.4

Deux mois seulement après la sortie d’Apache Camel en version 2.3.0, une nouvelle version 2.4.0 vient tout juste de sortir. Elle vient compléter un changelog déjà très fourni sur la version précédente et apporte son lot de corrections, améliorations et nouveautés :

  • Un support plus poussé d’OSGI avec la prise en charge de la norme Blueprint et la création d’un namespace XML adapté permettant de définir des routes Camel. Ce namespace est pour le moment uniquement exploitable via l’implémentation Blueprint ‘Apache Aries’ utilisée par le projet ‘Apache Karaf’. A noter que la définition d’errorHandler n’est pas possible pour le moment.
  • Un moteur de routage asynchrone non bloquant complet: tous les patterns d’intégration sont supportés ainsi que différents composants: JBI, NMR, CXF, Jetty et Netty.
  • La version de Spring utilisée par Camel passe à la 3.0.3 marquant ainsi la fin du support de la branche 2.0 (La version 2.5 étant encore supportée). Le support de Spring Security pour sa part change également de version et passe à la 3.0.3.RELEASE.

La version 2.3.0 proposait déjà de nombreuses améliorations majeures telles que le support des Property placeholders qui manquaient cruellement, ainsi que le support d’un nombre conséquent de nouveaux composants (camel-exec, camel-nagios, camel-netty, camel-spring-spring-security, camel-crypto, …).

Le projet Camel devrait à coup sûr continuer à faire parler de lui dans les prochains mois, tout en s’imposant un peu plus comme un acteur majeur dans le milieu des frameworks d’intégration.

Intégration de Grails avec Google App Engine

En navigant sur le blog « Le Touilleur Express« , je suis tombé sur un article passionnant décrivant l’intégration de Grails avec Google App Engine. Bien que l’article ait quelques mois, il n’en reste pas moins intéressant, et permet de se faire une bonne idée de la chose.

Une vidéo issue des « Google IO 2009″ présente Groovy, Grails, ainsi que son intégration avec le Googles App Engine. La vidéo parle d’elle même. Si vous connaissez déjà Groovy, vous pouvez sauter le premier tiers de la vidéo pour se focaliser sur l’intégration avec GAE.

Le lien vers l’article de Grails + Google App Engine:

Une introduction à Grails:

Un tutorial en trois parties basée sur une démo originale de Wicket:

Liens:

Si vous avez déjà créer une application basée sur Grails et tournant sur Google App Engine, n’hésitez pas à en faire part dans les commentaires.

Ajout du flux Twitter

Vous pouvez maintenant suivre mes Tweets depuis la sidebar du blog.

Pour accéder directement à mon flux Twitter, c’est par ici:

  • http://twitter.com/alexiskinsella

Vous pourrez y retrouver entre autre des tweets techniques ou bien des bonnes adresses. Ce widget Twitter vient compléter le widget Delicious qui recense déjà bon nombre de bookmarks techniques et de trouvailles du web.