lundi 8 avril 2013

Résoudre la version d'un artefact Maven avec Maven 3

Il peut être utile, dans le cadre d'un projet de résoudre la version d'un artefact lorsqu'une version générique est passée en paramètre (RELEASE, LATEST, x.x.x-SNAPSHOT).
Pour ce faire, il n'y a pas d'autre choix que de créer un plugin maison, pour enchaîner avec un traitement particularité.
Pour ce faire, il est nécessaire de se plonger dans la documentation volumineuse des API maven. De plus, l'API a évolué en version 3 (même si l'API v2 est toujours disponible).

Dans un premier temps, il faut créer le pom adéquat :
package org.test.maven.plugin;

import java.util.List;

import org.apache.maven.RepositoryUtils;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.sonatype.aether.RepositorySystem;
import org.sonatype.aether.RepositorySystemSession;
import org.sonatype.aether.resolution.ArtifactResolutionException;
import org.sonatype.aether.resolution.VersionRequest;
import org.sonatype.aether.resolution.VersionResolutionException;
import org.sonatype.aether.resolution.VersionResult;
import org.sonatype.aether.util.artifact.DefaultArtifact;

/**
 * Ce plugin affiche la version resolue d'un artefact
 * 
 * @goal version-artifact
 */
public class VersionArtefact extends AbstractMojo {
 /**
  * The entry point to Aether, i.e. the component doing all the work.
  * 
  * @component
  */
 private RepositorySystem repoSystem;

 /**
  * The current repository/network configuration of Maven.
  * 
  * @parameter default-value="${repositorySystemSession}"
  * @readonly
  */
 private RepositorySystemSession repoSession;

 /**
  * List of Remote Repositories used by the resolver
  * 
  * @parameter expression="${project.remoteArtifactRepositories}"
  * @readonly
  * @required
  */
 protected List<ArtifactRepository> remoteRepos;

 /**
  * Group Id of Artifact 
  * 
  * @parameter default-value="${project.groupId}"
  * @required
  */
 private String groupId;

 /**
  * Name of Artifact
  * 
  * @parameter default-value="${project.artifactId}"
  * @required
  */
 private String artifactId;

 /**
  * Version of Artifact
  * 
  * @parameter default-value="${project.version}"
  */
 private String version;

 /**
  * Type of Artifact (War,Jar,etc)
  * 
  * @parameter default-value="${project.packaging}"
  * @required
  */
 private String type;

 /**
  * Classifier for Artifact (tests,sources,etc)
  * 
  * @parameter default-value="${project.classifier}"
  */
 private String classifier;

 @Override
 public void execute() throws MojoExecutionException, MojoFailureException {
  try {   
   VersionResult version = getVersionArtifact() ;
   
   getLog().info(version.toString());
  } catch (VersionResolutionException e) {
   throw new MojoExecutionException("Impossible de recuperer la version de l'artefact", e);
  }
 }
 
 /**
  * Renvoi le fichier correspondant à l'artefact.
  * Prend en charge le téléchargement dans le repo local.
  *
  * @return le fichier demandé
  * @throws ArtifactResolutionException en cas d'erreur lors de la recherche de l'artefact
  */
    private VersionResult getVersionArtifact() throws VersionResolutionException {
  DefaultArtifact artifact = new DefaultArtifact(groupId, artifactId,
    classifier, type, version);
  
        VersionRequest request = new VersionRequest(artifact, RepositoryUtils.toRepos(remoteRepos), null);
        VersionResult version = repoSystem.resolveVersion(repoSession, request) ; 
  
        return version;
    } 
}

Il est nécessaire de récupérer les repositories Maven et l'interface permettant de les piloter.
L'interface s’appelle Aether.
Ensuite, le système fonctionne avec un principe de requête et de réponse.
Une requête avec l'artifactId, groupId... est créé, l'interface est appelé est le résultat est récupéré.

Attention ! Si l'artefact n'existe pas et qu'une version générique est passée en paramètre, un exception est levé. Sinon, la version renvoyée est celle donnée en paramètre.
Il n'y a donc pas moyen de savoir si l'artefact existe. En réalité si, mais vous ne trouverez rien sur ce sujet sur internet.
Pour cela, rendez-vous la semaine prochaine pour connaitre l'astuce.

Voici alors le code (très simple) :
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>org.test.maven.plugin</groupId>
    <artifactId>test-plugin-maven</artifactId>
    <packaging>maven-plugin</packaging>
    <version>1.0.0-SNAPSHOT</version>
 
 <build>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-plugin-plugin</artifactId>
    <version>3.0.4</version>
   </plugin>
  </plugins>
 </build>

 <dependencies> 
  <dependency>
   <groupId>org.apache.maven</groupId>
   <artifactId>maven-aether-provider</artifactId>
   <version>3.0.4</version>
   <type>jar</type>
   <scope>compile</scope>
  </dependency>

  <dependency>
   <groupId>org.apache.maven</groupId>
   <artifactId>maven-core</artifactId>
   <version>3.0.3</version>
   <scope>compile</scope>
  </dependency>
 </dependencies>

</project>

Ainsi, une version SNAPSHOT aura comme retour :
1.2.1-20121227.145212-1 @ nexus-libraries (http://xxxxxxx:8081/nxus/content/groups/public/, releases+snapshots)

lundi 1 avril 2013

Création d'un serveur en Java [2/2]

Dans l'article précédant, un rapide appeçu de comment créer un serveur en Java a été abordé.
Le principal inconvénient, c'est de ne pouvoir avoir qu'un client. Pas très pratique.

Il va donc être nécessaire de déléguer la réponse client à un thread.

Il y aura donc deux classes.
La première créer le serveur et écoute les connexions clientes.
La deuxième dialogue avec le client et est donc un thread.

La première classe :
package org.emeric.exemple.echo2;

import java.net.ServerSocket;


public class EchoServer {

    public static void main(String[] args) throws Exception {
        final ServerSocket socket = new ServerSocket(4444) ;
          
        while (true) {
            System.out.println("Waiting for client...");
            new EchoRequestHandler(socket.accept());
        }      
    }
}

La deuxième classe :
package org.emeric.exemple.echo2;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * Class to read request
 * 
 * @author Emeric MARTINEAU
 */
public class EchoRequestHandler extends Thread implements Runnable  {
    /**
     * Socket of client
     */
    private Socket socket ;
    
    /**
     * Constructor
     * 
     * @param s socket
     */
    public EchoRequestHandler(final Socket s)
    {
        socket = s;
        Thread t = new Thread(this) ;
        t.setDaemon(true) ;
        t.start() ;
    }
    
    /**
     * Read and response to the client
     */
    @Override
    public void run()
    {
        System.out.println("New client...") ;
        
        try {
            // Reader
            final BufferedReader in = new BufferedReader(new InputStreamReader(
                    socket.getInputStream()));
            
            // Auto flush, when println() call
            final PrintWriter out = new PrintWriter(new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream())), true);        
        
            String str = null ;
            do {
                // Read and wait if no data
                str = in.readLine() ;
                // Print
                out.println(str) ;
            } while (str != null) ;
            
            out.close() ;
            
            socket.close() ;
        } catch (final IOException e) {
            // TODO
        }
        
        System.out.println("Bye client...");
    }
}

Vous voilà prêt à dévolloper de nombreux serveurs en Java.

lundi 25 mars 2013

Création d'un serveur en Java [1/2]

La création d'un serveur java est relativement aisé.
En effet, la JRE met à disposition un ensemble de classe effectuant le plus gros du travail.

La classe java dédier à la partie serveur est ServerSocket. Elle permet d'écouter un port de la machine, puis d'attendre des clients.

Trois méthodes vont être intéressante getInputStream(), getOutputStream(), accept().

La méthode getInputStream() permet de lire les données du client.
La méthode getOutputStream() permet d'écrire au client.
La méthode accept() permet d'attendre l'arrivé de client.

Il est donc nécessaire de :
  1. créer un serveur,
  2. attendre un client,
  3. créer un stream pour la lecture des données client,
  4. créer un stream pour l'écriture vers le client.

Voic un exemple simple d'un serveur Echo qui affiche les données envoyés par le client :
package org.emeric.exemple.echo;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class EchoServer {

    public static void main(String[] args) throws Exception {
        System.out.println("Demarrage...");
        ServerSocket s = new ServerSocket(4444);
        Socket soc = s.accept();

        // Un BufferedReader permet de lire par ligne.
        BufferedReader plec = new BufferedReader(new InputStreamReader(
                soc.getInputStream()));

        // Un PrintWriter possède toutes les opérations print classiques.
        // En mode auto-flush, le tampon est vidé (flush) à l'appel de println.
        PrintWriter pred = new PrintWriter(new BufferedWriter(
                new OutputStreamWriter(soc.getOutputStream())), true);

        String str = null;

        do {
            // lecture du message
            str = plec.readLine(); 
            // renvoi d'un écho
            pred.println(str); 
        } while (str != null);

        plec.close();
        pred.close();
        soc.close();

        System.out.println("Fin");
    }
}

Ce serveur est très basique car il n'accepte qu'un client.
Dans la prochain étape, ce serveur sera multi-threadé pour accèter plusieurs clients.

lundi 11 mars 2013

Fusion bus JMX de la JVM et JBoss

La technologie JMX dans un environnement Java, est une API qui permet de gérer une application en cours d'éxécution.

Cette API est disponible au niveau JVM. On parle du bus JMX de la JVM.
On peut voir ce bus via la jConsole.

Le serveur d'application JBoss embarque son propre serveur JMX. De ce fait, les informations publiées par défaut sur le bus JMX au sein de JBoss, ne sont pas visibles sur le bus JMX de la JVM.

Depuis JBoss 4.0.3, il est possible de les rassembler, en ajoutant les options suivantes :
# Intégrer JMX de JBoss 4 et de JVM (à partir de JBoss 4.0.3)
JAVA_OPTS="$JAVA_OPTS -Djboss.platform.mbeanserver -Djavax.management.builder.initial=org.jboss.system.server.jmx.MBeanServerBuilderImpl"

# Intégrer JMX de JBoss 5 et de JVM
JAVA_OPTS="$JAVA_OPTS -Djboss.platform.mbeanserver"
Source : JTips

lundi 4 mars 2013

Ajouter un propriété dynamiquement dans un plugin maven

Il peut être parfois utile dans un plugin maven, développé par vos soins, d'ajouter des properties qui pourront être ensuite utilisée dans le pom.xml.

Pour ce faire, rien de bien compliqué.
Il est nécessaire de récupérer l'objet projet de Maven et d'ajouter un propriété :
// injection de l'objet project
/** @parameter default-value="${project}" */
private org.apache.maven.project.MavenProject project ;

// et dans la méthode execute(), ajouter :
project.getProperties().setProperty("maPropriete", maValeur) ;

lundi 25 février 2013

Closing a connection for you. Please close them yourself

Voici un message souvent rencontré sur les projets type JEE.
Que veut dire ce mystérieux message ? Qui l'écrit ?

Tout d'abord, sachez que se message est produit par JBoss, pour être exact, ce message est écrit par le CachedConnectionManager de JBoss, qui encapsule Hibernate.
Il s'agit d'un pool de connexion aux bases de données.

Il écrit ce message, lorsqu'il supprime une connexion de son pool parce qu'elle n'est plus utilisée alors qu'elle est encore ouverte (ce qui est contradictoire donc).

Le code suivant ne pose aucun problème, car on ferme tout :
Connection aConnection = null; /* Connexion à la base de données */
Statement aStatement = null; /* La requête */
ResultSet aResultSet = null; /* résultat de la requête */

try
{
    aConnection = getConnectionDansArbreJndiJBoss() ;
    aStatement = aConnection.prepareStatement(requete) ;
    aResultSet = aStatement.executeQuery(requete);
}
catch (SQLException e)
{
  ...
}
finally
{
    aResultSet.close(); // aResultSet != null !!!!
    aStatement.close(); // aStatement != null !!!!
    aConnection.close(); // aConnection != null !!!!
}

mais enlevez la aConnection.close() et vous verrez apparaître le message !

La difficulté avec ce message est d'identifier l'élément posant problème.

lundi 11 février 2013

Connaitre les requêtes exécutées par session sous Oracle

Afin de diagnostiquer des problèmes, il peut être opportun de connaître les requêtes exécutés par une session. Voici un exemple tiré de ce site http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:497421739750 :
COLUMN STATUS format a10
SET feedback off
SET serveroutput ON
 
COLUMN username format a20
COLUMN sql_text format a55 word_wrapped
 
SET serveroutput ON size 1000000
declare
    x number;
begin
    FOR x IN
    ( SELECT username||'('||sid||','||serial#||
                ') ospid = ' ||  process ||
                ' program = ' || program username,
             to_char(LOGON_TIME,' Day HH24:MI') logon_time,
             to_char(sysdate,' Day HH24:MI') current_time,
             sql_address, LAST_CALL_ET
        FROM v$session
       WHERE STATUS = 'ACTIVE'
         AND rawtohex(sql_address) <> '00'
         AND username IS NOT NULL ORDER BY last_call_et )
    loop
        FOR y IN ( SELECT max(decode(piece,0,sql_text,NULL)) ||
                          max(decode(piece,1,sql_text,NULL)) ||
                          max(decode(piece,2,sql_text,NULL)) ||
                          max(decode(piece,3,sql_text,NULL))
                               sql_text
                     FROM v$sqltext_with_newlines
                    WHERE address = x.sql_address
                      AND piece < 4)
        loop
            IF ( y.sql_text NOT LIKE '%listener.get_cmd%' AND
                 y.sql_text NOT LIKE '%RAWTOHEX(SQL_ADDRESS)%')
            then
                dbms_output.put_line( '--------------------' );
                dbms_output.put_line( x.username );
                dbms_output.put_line( x.logon_time || ' ' ||
                                      x.current_time||
                                      ' last et = ' ||
                                      x.LAST_CALL_ET);
                dbms_output.put_line(
                          substr( y.sql_text, 1, 250 ) );
            end IF;
        end loop;
    end loop;
end;
/
Merci à Drazzib pour l'astuce.