Qu'est ce qu'on se fait ch ... !

Aller au contenu | Aller au menu | Aller à la recherche

Java EE

Un peu de tout sur la plateforme JAVA EE

Fil des billets - Fil des commentaires

Utilisation des API Google

Dans le cadre de mon appli jsf sur gae, j'ai à interagir avec le service de calendrier google. Voici les actions que j'ai menées pour installer et faire fonctionner le bousin:

Installation du client GDATA dans le référentiel MAVEN

 $ mvn install:install-file -DgeneratePom=true -DgroupId=com.google.gdata -DartifactId=gdata-core
 -Dpackaging=jar -Dfile=gdata-core-1.0.jar -Dversion=1.0


$ mvn install:install-file -DgeneratePom=true -DgroupId=com.google.gdata -DartifactId=gdata-client
 -Dpackaging=jar -Dfile=gdata-client-1.0.jar -Dversion=1.0

 $ mvn install:install-file -DgeneratePom=true -DgroupId=com.google.gdata -DartifactId=gdata-calendar
 -Dpackaging=jar -Dfile=gdata-calendar-2.0.jar -Dversion=2.0



Ajout dans le fichier pom.xml

<dependency>
            <groupId>com.google.gdata</groupId>
            <artifactId>gdata-core</artifactId>
            <version>1.0</version>
        </dependency>
 
        <dependency>
            <groupId>com.google.gdata</groupId>
            <artifactId>gdata-client</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.gdata</groupId>
            <artifactId>gdata-calendar</artifactId>
            <version>2.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.collections</groupId>
            <artifactId>google-collections</artifactId>
            <version>1.0</version>
        </dependency>

Identification

La partie la plus subtile. Trois choix sont possibles, une authentification par token, par login/password et par openid. J'ai choisi de gérer le token car mon application va utiliser la gestion des comptes google.

J'ai choisi de créer une servlet qui fait les actions suivantes : Proposer une page qui redirige vers la connexion google, récupération du token et stockage en base pour chaque utilisateur.

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String tokenParameter = req.getParameter("token");
        if (tokenParameter == null) {
            req.getRequestDispatcher("/faces/retreiveToken.xhtml").forward(req, resp);
        } else {
            try {
//procedure google pour recuperer le token et le rendre permanent
                String onetimeUseToken = AuthSubUtil.getTokenFromReply(req.getQueryString());
                String sessionToken = AuthSubUtil.exchangeForSessionToken(onetimeUseToken, null);
                Logger.getLogger(TokenServlet.class.getName()).log(Level.INFO, "Token recupéré");
 
                UserManagedBean userManagedBean = (UserManagedBean) req.getSession(false).getAttribute("userManagedBean");
//assignation du token
                User user = userManagedBean.getUser(req);
                user.setToken(sessionToken);
//persistence
                BusinessService<User> userService = new BusinessServiceImpl<User>();
                userService.update(user, user.getId());
            } catch (GeneralSecurityException ex) {
                Logger.getLogger(TokenServlet.class.getName()).log(Level.SEVERE, null, ex);
            } catch (AuthenticationException ex) {
                Logger.getLogger(TokenServlet.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

La page de connexion est assez simple :

<ui:composition template="./standardtpl.xhtml">
            <ui:define name="content">
                <h:form prependId="false">
                    <p>MyApp needs access to your
                        Google Calendar account to read your Calendar feed. To authorize
                        MyApp to access your account,
                        <a href="#{userManagedBean.requestUrl}">Connexion sur google</a>
                    </p>
                </h:form>
            </ui:define>
        </ui:composition>

Le lien de connexion se crée de la manière suivante

public String getRequestUrl() {
        StringBuffer request = new StringBuffer(FacesContext.getCurrentInstance().getExternalContext().getRequestScheme()).append("://").append(FacesContext.getCurrentInstance().getExternalContext().getRequestServerName()).append(":").append(FacesContext.getCurrentInstance().getExternalContext().getRequestServerPort()).append("/tokenServlet");
        return AuthSubUtil.getRequestUrl(request.toString(),
                "http://www.google.com/calendar/feeds/",
                false,
                true);
 
    }

Conclusion

Je m'arrêterai la dans les exemples car la documentation est déjà bien détaillée sur ce sujet. Quoi qu'il en soit, je trouve que chez google, a défaut d'oeuvrer pour les données privées de ses utilisateurs, il savent très bien faire des API et les documenter :)

Seule limitation, la possibilité de faire des tests unitaires avec le token. La gestion du retour avec un proxy n'est pas possible (menfin à ce que j'ai vu ...)

Write once , run everywhere ... sauf sur google app engine

Après la mise à jour des différents composants ( GAE #1.3.5 , primefaces 2.1RC1, ...) me voila reparti à re-développer une appli sur GAE. Après quelques galères tests unitaires, j'ai pu me rendre compte des nombreuses limitations à JPA / GAE.

Les requêtes

D'abord, seul JPA V1 est implémenté. Ça a l'air con comme ca, mais par exemple on ne peut pas utiliser les CriteriaQuery alors que l'API JDO fournie par Google fournit les Filter.Après , me direz vous, c'est pas l'extase, ca ne fait pas exactement la même chose, on peut coder directement les critères en JPQL. Mais voila dès que vous voulez faire des recherches un peu larges avec par exemple un formulaire de recherche à critères multiples, vous pouvez obtenir l'erreur suivante :

testcase: testFindLike(info.touret.mycellar.test.BottleTest):        Caused an ERROR
Problem with query <SELECT A FROM info.touret.winecellar.pojo.Bottle A WHERE A.name like 'BOUTEILLE%'  or A.vintage like '2001%'>: Or filters cannot be applied to multiple properties (found both name and vintage).
org.datanucleus.store.appengine.query.DatastoreQuery$UnsupportedDatastoreFeatureException: Problem with query <SELECT A FROM info.touret.winecellar.pojo.Bottle A WHERE A.name like 'BOUTEILLE%'  or A.vintage like '2001%'>: Or filters cannot be applied to multiple properties (found both name and vintage).
        at org.datanucleus.store.appengine.query.DatastoreQuery.addLeftPrimaryOrExpression(DatastoreQuery.java:1131)
        at org.datanucleus.store.appengine.query.DatastoreQuery.addLeftPrimaryExpression(DatastoreQuery.java:1105)
        at org.datanucleus.store.appengine.query.DatastoreQuery.addPrefix(DatastoreQuery.java:931)
        at org.datanucleus.store.appengine.query.DatastoreQuery.handleMatchesOperation(DatastoreQuery.java:891)
        at org.datanucleus.store.appengine.query.DatastoreQuery.addExpression(DatastoreQuery.java:864)
        at org.datanucleus.store.appengine.query.DatastoreQuery.addExpression(DatastoreQuery.java:835)

Un peu bête ...

Les jointures et fetching

Sur ce sujet, je me suis arraché les cheveux pas mal de temps. Exemple : ne Jointure OneToMany ne me ramenait pas du tout les entités en question lors d'un select. Que faire ?? Après quelques recherches sur la toile, je me suis rendu à l'évidence, que GAE ne gérait que les jointures via les clés primaires. Oubliez les belles jointures bi directionnelles et uni directionnelles JPA . L'insertion , la modification ne peut s'effectuer que par les clés primaires

Exemple avec une relation onetoone

@OneToOne(cascade = {CascadeType.REFRESH})
    private Key producer;

Mais il est possible de "hacker" la matrice en rajoutant un autre attribut à notre classe qui aurait la configuration suivante :

@OneToOne(cascade = {CascadeType.REFRESH})
    @Column(name = "producer", insertable = false, updatable = false)
    private Producer producerAlias;

Donc pour les insertions, suppressions, nous sommes obligés de passer par l'instance de la classe Key, par contre, une sélection passerait par l'alias. Cette manipulation permet de gérer la jointure ( avec fetch !) directement au niveau de la requête JPQL.

Exemple :

Query query = em.createQuery("select from Bottle b join b.producerAlias");

C'est un peu biaisé, mais bon ca simplifie la vie au niveau des requêtes.

Après, ce n'est que mon avis, je me suis trouvé pas mal obligé de dé-normaliser mes relations entre entités. Par exemple, je me suis mis dans l'idée de faire un nuage de tags. Bien au lieu de créer un pojo tag qui serai persisté directement dans big table, j'ai préféré créer un attribut tagline pour mon entité maître. Ca m'a pris moins de temps à créer. après va falloir que j'optimise les requêtes par une gestion de cache par exemple.

Conclusion :

Quand on développe sur GAE, il faut à mon avis bien penser aux contraintes de cette plateforme, surtout sur la persistance des données. Le développeur JAVAEE habitué à hibernate/jpa peut vite pédaler dans la choucroute au début. A mon avis ( et pas que ) JDO est à préférer. l'API semble connaître moins de limites.

Pas mal de plaintes on été faite à ce sujet. Je viens de voir un article sur une recherche full text. A voir ...

Et non je n'ai pas changé

Après quelques jours semaines mois passés à faire autre chose ... mais faut bien vivreeeee.... moi voila revenu sur mon projet de cellier numérique et regardez l'erreur maven que j'obtiens :

Copying webapp resources[D:\java\src\my-cellar\src\main\webapp]
------------------------------------------------------------------------
[ERROR]FATAL ERROR
------------------------------------------------------------------------
Negative time
------------------------------------------------------------------------
Trace
java.lang.IllegalArgumentException: Negative time
        at java.io.File.setLastModified(File.java:1258)
        at org.apache.maven.plugin.war.packaging.AbstractWarPackagingTask.copyFile(AbstractWarPackagingTask.java:295)
        at org.apache.maven.plugin.war.packaging.AbstractWarPackagingTask$1.registered(AbstractWarPackagingTask.java:150)

Si c'est pas un signe pour que je parte en week end....

Google App Engine / JSF 2 / Facelets / Primefaces

Retour sur GWT

Après quelques semaines sur GWT, je me suis vite lassé. Si on n'a pas d'éediteur graphique digne de ce nom ou une librairie intéressante de widget,on perd pas mal de temps à réaliser des applications dignes de ce nom ( à moins de s'appeler google biensûr...) Je suis donc revenu aux fondamentaux et me voila revenu sur JSF. J'en ai profité pour tester la version 2.0 sur Google App Engine. Et la c'est le drâme. C'est un vrai parcours du combattant ou un champ de mines, à vous de voir.

Problèmes rencontrés

Pas mal de problèmes que j'ai eu ont été résolus en me référant sur ce tutoriel ou sur cette page chez google

Pour faire simple, voici un résumé des problèmes

  • La version 1.3.2 de l'appengine ne fonctionnait pas chez moi avec netbeans :-( Obligé de passer en 1.3.1
  • L'implémentation standard de JSF2 n'est pas supporté par défaut dans GAE car elle utilise la classe InitialContext qui n'est pas supportée.
  • Il y a pas mal de paramètres à ajouter dans le web.xml ( cf ci-après )
  • Pas de page de debug de facelets car nous sommes obligé de travailler dans un mode différent de DEVELOPMENT
  • Le scope VIEW n'est pas "trop" supporté
  • Problème XALAN ( voir après)

Dépendances à ajouter

Commons-logging

Petite dépendance nécessaire et qui n'apparaît qu'à l'éxecution

log4j

Idem

facestrace

Très utile pour le debug. De plus ce composant est bien intégré à primefaces. Il suffit d'ajouter le paramètre de requête HTTP trace=true pour que la console facestrace apparaisse.

xalan

Si comme moi vous avez l'erreur suivante

    com.sun.faces.config.ConfigurationException: CONFIGURATION FAILED! 
    com.sun.org.apache.xalan.internal.xsltc.runtime.BasisLibrary is a restricted class. Please see the Google App 
    Engine developer's guide for more details.

Vous devez ajouter les librairies serializer.jar et xalan.jar provenant du projet xalan dans le répertoire WEB-INF/lib de votre webapp.

Ma Configuration

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <display-name>
        wine cellar
    </display-name>
    <description>
        wine cellar
    </description>
    <!-- ***** GAE 1.3.0 appears to handle server-side state saving. *****  -->
    <context-param>
        <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
        <param-value>client</param-value>
    </context-param>
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Production</param-value>
    </context-param>
    <context-param>
        <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
        <param-value>.xhtml</param-value>
    </context-param>
    <context-param>
        <param-name>com.sun.faces.expressionFactory</param-name>
        <param-value>com.sun.el.ExpressionFactoryImpl</param-value>
    </context-param>
    <context-param>
        <description>
            Set this flag to true if you want the JavaServer Faces
            Reference Implementation to validate the XML in your
            faces-config.xml resources against the DTD. Default
            value is false.
        </description>
        <param-name>com.sun.faces.validateXml</param-name>
        <param-value>true</param-value>
    </context-param>
    <!-- ***** Accommodate Single-Threaded Requirement of Google AppEngine  -->
    <context-param>
        <description>
            When enabled, the runtime initialization and default ResourceHandler
            implementation will use threads to perform their functions. Set this
            value to false if threads aren't desired (as in the case of running
            within the Google Application Engine).
 
            Note that when this option is disabled, the ResourceHandler will not
            pick up new versions of resources when ProjectStage is development.
        </description>
        <param-name>com.sun.faces.enableThreading</param-name>
        <param-value>false</param-value>
    </context-param>
    <!-- primefaces -->
    <context-param>
        <param-name>com.sun.faces.allowTextChildren</param-name>
        <param-value>true</param-value>
    </context-param>
    <!-- Faces Servlet -->
    <context-param>
        <param-name>com.sun.faces.enableMultiThreadedStartup</param-name>
        <param-value>false</param-value>
    </context-param>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet>
        <servlet-name>Resource Servlet</servlet-name>
        <servlet-class>
            org.primefaces.resource.ResourceServlet
        </servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Resource Servlet</servlet-name>
        <url-pattern>/primefaces_resource/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>/faces/*</url-pattern>
        <url-pattern>*.jsf</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>index.xhtml</welcome-file>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>

Le fichier faces-config.xml

<faces-config version="2.0"
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<application>
    <locale-config>
      <default-locale>en</default-locale>
    </locale-config>
  </application>
 
    <managed-bean>
...
    </managed-bean>
 
</faces-config>

Limitations

Et oui il y a des limitations à tout ça : L'un des gros plus de la spec JSF2 est la scope view qui permet de conserver l'état d'un composant au sein d'une page dans n'importe quelle étape du cycle de vie JSF. Bref, c'est entre le scope session et le scope request. GAE ne semble pas trop le supporter. Il est même préférable de n'utiliser que le scope request. Personnellement j'ai du adapter mon design à un mode réellement sans état. GAE ne supporte pas le PROJECT_STAGE Development Il faut donc le paramétrer tel quel

<context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Production</param-value>
    </context-param>

Cette configuration semble occulter la page d'erreur standard de facelets qui est très utile pendant le développement. Heureusement facestrace est la.

Enfin, l'une des grosses limitations est la suppression des données entre chaque arrêt/relance du SDK GAE. Je n'ai pas encore cherché trouvé comment les garder en mémoire.

Conclusion

A cette armé mexicaine de composants, il ne me manque qu'une solution d'IoC ( probablement CDI ) et surement un framework d' AOP. GAE fournit déjà pas mal de composants permettant de gérer les objets en mémoire et d'améliorer les perfs.

La suite dans un prochain numéro...

Test de la plateforme Google App Engine

Dans la série mieux vaut tard que jamais. Me voici décidé à tester la plateforme Google App Engine dans sa version JAVAEE. J'ai donc trouvé une idée d'application assez bidon qui pourrait être représentative du monde réel et sortir d'un simple Hello World. Vous allez voir que je pars de loin :) J'ai choisi d' aborder plein de technos que je ne connaissais pas ou de loin :

L'environnement de développement

Afin de ne pas trop me prendre la tête dans un premier temps avec l'intégration MAVEN et NETBEANS ( c'est mon éditeur de prédilection ) J'ai donc opté pour la solution standard Google : le plugin eclipse. Je ne détaillerai pas la procédure d'install car ce n'est pas le but de cet article. Tout se passe comme dans la doc. Je sais, ca peut faire un peu bizarre de prime abord, mais ca fonctionne :)

Après installation et au premier lancement d'une application démo, un plugin firefox (pour la version 3.5. La version 3.6 n'est encore disponible) et/ou un plugin IE. Ma première impression fut la qualité de l'intégration entre le browser et l'IDE. Les modifications sont prises en compte à chaud. J'ai pas ou très peu besoin de rafraîchir le navigateur. Bref, ce n'est que du bonheur .... Seule ombre au tableau: il manque par défaut un wizard WYSIWYG pour l'édition des écrans. Obligé par défaut de tout se coller à la mi-mine. Ce n'est pas très dur mais ca peut être un frein pour pas mal de gens.

GWT

Développeurs Swing, vous pouvez dorénavant faire des interfaces WEB :D ! Ayant fait un peu de swing il y a quelques années de ça (putain, je commence à me faire vieux...), je retrouve pas mal d'automatismes sur l'assemblage des composants dans des panels avec la gestion des layouts. C'est stable. Franchement, je préfère cette manière de développer des GUI. Je trouve que JSF a manqué l'occasion de faire un vrai développement à base de composants ( même si ca s'est amélioré en version 2.0). Il y a un vrai binding composant/modèle.

Mais j'ai encore quelques reproches à faire (juste pour faire mon chieur):

A l'instar de JSF, la bibliothèque de composants est assez pauvre en standard. Certes, largement plus fournie que l'implémentation de base fournie par SUN mais je m'attendais à créer des applications GMAIL like avec l'implémentation fournie par Google. Bon il existe quand même des librairies de composants qui étendent GWT. Mais bon attention, toutes ne gèrent pas convenablement le JAVASCRIPT.

L'accessibilité n'est pas encore totalement gagné. Certes il existe l'infrastructure technique. Depuis la version 1.5, c'est totalement intégré à GWT, cependant, il n'y a aucune garantie pour les bibliothèques tierces.

Guice

L'introspection de ce framework faisait parti de ma TODO LIST depuis pas mal de temps.

L'intégration dans un environnement WEB est très simple. Après avoir crée un listener

public class MyGuiceServletContextListener extends
		GuiceServletContextListener {
	@Override
	protected Injector getInjector() {
		Injector injector = Guice.createInjector(new MyModule(),
				new EMModule());
		return injector;
	}
}

et correctement initialisé le fichier web.xml

<filter>
		<filter-name>guiceFilter</filter-name>
		<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
	</filter>
 
	<filter-mapping>
		<filter-name>guiceFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<listener>
		<listener-class>info.touret.my.MyGuiceServletContextListener</listener-class>
	</listener>

Là, j'ai peut être loupé quelque chose, mais le binding se fait par le code

Il ne me restait plus qu'à utiliser les fameuses annotations Guice @Provides et @Inject Bref, GUICE?C'est simple. Ça fait peut de choses , mais ca le fait bien...

Google App Engine

L'un des gros manques de JAVA était la présence d'hébergeurs fournissant une stack JAVAEE. Maintenant c'est chose faite, grâce à Google. L'ensemble est clair et cohérent. GAE n'oblige pas à utiliser GWT. Je crois que l'on peut également utiliser JSF avec facelets. Seul bémol à mon sens, c'est le stockage des données. On ne peut pas - si je me trompe, n'hésitez pas à le mettre en commentaire- visualiser les données aisément. Le chargement est possible grâce à un script python. Quid de la pérennité des données stockées sur le serveur de google, pour faire des retours arrières simplement, etc etc. Bref des questions que l'on se pose dès que l'on travaille de manière un peu plus professionnelle et que l'on veut vendre un service à des clients.

Personnellement, je vois très bien ce genre de plateforme utilisée pour les PME qui font appel à des indépendants pour réaliser des WEBAPPS et les héberger à moindre cout. ( cloud quand tu nous tiens...)

Pourquoi je suis passé sur Netbeans

Voila quelques raisons qui m'ont fait choisir netbeans comme mon ide de prédilection :

La gestion des plugins est beaucoup plus intégré

Pas besoin d'aller sur un milliard de sites pour récupérer un plugin. Tout est centralisé!

Le support de Maven est natif

Et oui, par rapport à eclipse, le support des projets maven est natif. Quand vous ouvrez un projet maven, netbeans prend en compte automatiquement la configuration des fichiers pom.xml et substitue maven aux builds ant livrés par défaut.

Une seule gestion du classpath

C'est la ou eclipse pêche.... Dans un vrai projet, il y a plusieurs niveaux de classpath à gérer : le classpath pris en compte dans eclipse et celui de maven. Vu que la majorité des problèmes JAVA est lié à la configuration et la gestion du classpath, ca m'économise pas mal de temps.

C'est roots

Oui, eclipse /wtp est peut être trop intégré pour moi, j'aime assez être proche de la configuration cible ( build maven) et exclure de mes résolutions de pb l'IDE.

Ce qui manque encore

  • Pas mal de fonctionnalités d'édition et refactoring où eclipse est encore loin devant. Peut être qu' intellijidea est meilleurs mais bon netbeans est libre et gratuit.
  • Un support facelets digne de ce nom
  • Un support seam :D
  • Une bonne complétion dans les annotations JPA et JSF
  • Un support de tous les frameworks JSF dans l'editeur graphique

Un quatrième profil de binding pour JBOSS AS

Juste un post pour ceux qui souhaitent rajouter un quatrième profil dans le fichier de binding de JBOSS .

Voila un profil qui fonctionne. enjoy

Le plugin maven jaxws en action

Voila résumé en quelques lignes les différentes manipulations que j'ai effectué sur mon projet maven pour intégrer des services web:

D'abord j'ai crée un module à part entière qui centralise les sources générés à partir des fichiers WSDL. Dans ce projet de type jar

	<!-- Paramètres généraux -->
    <modelVersion>4.0.0</modelVersion>
    <artifactId>wsclient</artifactId>
    <name>wsclient</name>
    <description />
    <packaging>jar</packaging>
    <version>2.1.2-SNAPSHOT</version>
j'ai attaché à la phase generate-sources la génération des STUBS à partir des fichiers WSDL.

Génération des couches d'appel

La seule configuration "exotique" que j'ai apporté est la configuration par package . Pour faire bref, un package = 1 service web. Logiquement, on n'a pas trop à faire ca car les objets doivent être factorisés. Mais bon vu la techno préhistorique que j'ai en face, je n'ai pas trop le choix

Pour que le plugin jaxws s'exécute bien, il faut faire la configuration suivante :

 <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>jaxws-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>01</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>wsimport</goal>
                        </goals>
                        <configuration>
                            <verbose>true</verbose>
                            <packageName>fr.monpackage.ws.1</packageName>
                            <sourceDestDir>${basedir}/src/main/java</sourceDestDir>
                            <staleFile>${project.build.directory}/jaxws/stale/.stale1Flag</staleFile>
                            <wsdlFiles>
                                <wsdlFile>01.wsdl</wsdlFile>
                            </wsdlFiles>
                        </configuration>
                    </execution>
                    <execution>
                        <id>02</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>wsimport</goal>
                        </goals>
                        <configuration>
                            <verbose>true</verbose>
                            <packageName>fr.monpackage.ws.2</packageName>
                            <staleFile>${project.build.directory}/jaxws/stale/.stale2Flag</staleFile>
                            <sourceDestDir>${basedir}/src/main/java</sourceDestDir>
                            <wsdlFiles>
                                <wsdlFile>02.wsdl</wsdlFile>
                            </wsdlFiles>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

Remarque : la personnalisation du fichier stale est obligatoire

Nettoyage des fichiers générés

Pour automatiser la suppression des fichiers générés au moment de l'appel du plugin clean ,j'ai apporté la configuration suivante :

 <plugin>
                <artifactId>maven-clean-plugin</artifactId>
                <configuration>
                    <filesets>
                        <fileset>
                            <directory>${basedir}/src/main/java</directory>
                            <includes>
                                <include>**/**</include>
                            </includes>
                        </fileset>
                        <fileset>
                            <directory>${basedir}/src/wsdl</directory>
                            <includes>
                                <include>*.wsdl</include>
                            </includes>
                        </fileset>
                    </filesets>
                </configuration>
            </plugin>

Inclusion dans l'EAR

A l'instar des autres JARS, j'ai inclus l'artifact wsclient dans mon ear en tant que MODULE JAR

                        <jarModule>
                           <groupId>monpackage</groupId>
                           <artifactId>wsclient</artifactId>
                           <bundleFileName>wsclient.jar</bundleFileName>
                           <includeInApplicationXml>true</includeInApplicationXml>
                       </jarModule>

...

                   </modules>

Vu que je suis sous JBOSS, je n'ai pas à m'occuper des inclusions de CLASSPATH dans les fichiers MANIFEST :)

JAVA EE vs Services WEB & SIEBEL

Quand on fait des services web on se dit, c'est cool l'intéropérabilité!!! Surtout quand des gars de l'avant vente font éloge de la rolls des CRM ( quand je compare siebel à une rolls, c'est parce que c'est très cher et très lourd, comme la voiture)

Bref, voici en quelques mots les écueils que j'ai rencontré et rencontre encore actuellement:

Les standards

Tout d'abord, il a fallu se pencher sur le support des standards. La je me suis dit on part sur de bonnes bases :

  • SOAP 1.1 ( un peu vieux, mais bon je n'ai pas à gérer d'attachements ) ,
  • WS-I BASIC PROFILE 1.1.

A ce niveau on était compatible avec JAXWS et la stack de JBOSSWS 2.0.5 ( livrée avec JBOSS AS 4.2.3.)

Les schémas de données

Le premier problème est venu avec la gestion des données: SIEBEL me fournit à chaque fois un WSDL composé de 2 ou 3 schémas XML qui se font référence mutuellement. Et ca ... WSIMPORT n'aime pas trop. Pour preuve, l'erreur :

src-resolve: Cannot resolve the name  to a(n) 'element declaration' component

La seule résolution que j'ai trouvée était de placer tous les objets dans le même namespace et de fusionner les différents schémas. Je sais ca fait brutal, mais je n'ai trouvé que ca pour l'instant....

La gestion des virtual host

Le but ultime serait de faire tourner tout ce petit monde derrière un frontal web avec un virtual host qui changerait de nom selon l'environnement ( transcendant n'est-ce pas ?) Le seul moyen que j'ai trouvé pour l'instant, et qui ne me plait guère, est d'utiliser une annotation propriétaire JBOSSWS : @WebContext . J'essaye de voir sur les forums si il existe une autre solution ...

Réinitialiser la bonne version d'un projet dans MAVEN

Il arrive - je vous demande un maximum d'imagination :-) - que vous ayez échoué la première exécution de la commande mvn release:prepare et que même après avoir lancé un mvn release:clean, les fichiers pom.xml soient vérolés et n'indiquent pas la bonne version du projet exemple :


<version>2.0.1</version>

et non


<version>2.0.1-SNAPSHOT</version>

Pour rétablir, soit vous le faites à la main dans tous les fichiers. Dans le cas d'un projet JAVA EE, ca peut s'avérer fastidieux ou le faire avec PERL :-) Voici la ligne de commande

 $ find . -name "pom.xml" -exec perl -p -i.old -e "s/2.0.1/2.0.1-SNAPSHOT/g" {}\;

En espérant que ca aidera quelques développeurs malchanceux dans leur versions du vendredi soir !

- page 1 de 5