Articles avec #java tag

Publié le 21 Août 2015

Cette erreur survient lorsque la JVM utilise beaucoup de CPU pour ne vider qu'une petite partie de la mémoire. Par exemple lorsque 98% de CPU ne parvient pas à libérer plus de 2% de mémoire. Dans ce cas la JVM lance ce type d'OutOfMemoryError.

Ceci ne doit se produire que dans de rare cas où un programme java crée énormément d'objets temporaires.

On peut désactiver cette erreur en utilisant l'option suivante:

-XX:-UseGCOverheadLimit

Plus d'info disponible dans cet article: Excessive GC Time and OutOfMemoryError

Voir les commentaires

Rédigé par Bliz

Publié dans #Java

Repost 0

Publié le 6 Mars 2015

J'ai dernièrement rencontré cette erreur en testant une appli assez conséquente.

Comme l'erreur est une OutOfMemoryError j'ai naturellement penser à augmenter la mémoire allouée à la JVM mais cela est resté sans effet.

En fait la mémoire n'est pas le problème dans ce cas là. Il faut bien lire le message remonter par cette erreur:

unable to create new native thread

Cela signifie que la JVM ne peut créer de nouveau thread car la limite de l'OS est atteinte.

Souvent cela indique une mauvaise gestion des threads.

Ce qui était malheureusement le cas ici: beaucoup de tests en parallèles bloquait des thread sur des requêtes HTTP.

Voir les commentaires

Rédigé par Bliz

Publié dans #Java

Repost 0

Publié le 4 Juillet 2014

Voilà un terme plutôt impressionnant. Enfin c'est ce que je me disais avant de comprendre de quoi il s'agissait mais en fait c'est un principe assez simple qui se cache derrière ces mots.

 

Commençons par un peu de code pour illustrer le principe. Prenons une classe qui utilise un service quelconque:

public class Traducteur {

  public String traduit(String phrase) {
    // TraductionService peut par exemple appeler
    // un service de traduction en ligne
    // comme par exemple Google Translate

    return GoogleTraductionService.translate(phrase)
  }

}

C'est volontairement très (trop) simple mais nous avons ici une classe (Traducteur) qui utilise une autre classe (GoogleTraductionService) pour effectuer une tâche (une traduction).

Jusqu'ici tout va bien mais nous avons introduit un couplage fort entre le traducteur et le service de traduction.

Vous ne voyez toujours pas le problème ... et bien essayons d'écrire un test unitaire pour notre traducteur :

public class TraducteurTest {

  Traducteur traducteur = new Traducteur();

  @Test
  public void testTraduction() {
    String traduction = traducteur.traduit("voiture");
    Assert.equals("car", traduction);
  }

}

 

Imaginons que notre TraductionService appelle l'api de Google Translate et qui nous soyons connecté à internet. Le test passe. Pas de problème!

Oui mais voilà imaginons que nous n'ayons plus de connection internet, le test est KO. Et oui c'est un test unitaire le but n'est pas de tester notre TranslationService et encore moins l'API de Google!

Le problème c'est que dès qu'on appelle la méthode "traduit()" on se retrouve avec notre GoogleTranslationService.

Comment faire? Tout simplement introduire une interface et passer le service au constructeur ce qui donne:

public class Traducteur {

  private TranslationService service;

  public Traducteur(TranslationService service) {
    this.service = service;
  }

  public String traduit(String phrase) {
    return service.translate(phrase);
  }

}

Voilà maintenant notre Traducteur peut fonctionner avec n'importe quel service de traduction du moment qu'on a une implementation qui respecte notre interface.

Et pour notre test ... et bien on peut écrire notre propre service :

public class TraducteurTest {

  static class TraductionServiceBouchon implements TraductionService {

    public String traduit(String phrase) {
      if ("voiture".equals(phrase)) return "car";
      else return "";
    }

  }

  TraducteurService service = new TraductionServiceBouchon();
  Traducteur traducteur = new Traducteur(service);

  @Test
  public void testTraduction() {
    String traduction = traducteur.traduit("voiture");
    Assert.equals("car", traduction);
  }

}

Et voilà nous venons d'effectuer une injection de dépendance. Nous avons injecté le service de traduction (notre dépendance) dans le constructeur du Traducteur.

Et nous avons gagné en souplesse: on peut utiliser notre traducteur avec n'importe quelle implémentation de TraductionService et notre test unitaire est vraiment unitaire.

 

Bref si vous écrivez de bon tests unitaires il y a de fortes chances que vous fassiez de l'injection de dépendance sans vraiment le savoir.

Voir les commentaires

Rédigé par Bliz

Publié dans #Java

Repost 0

Publié le 10 Septembre 2013

Je ne suis pas vraiment fan des mocks mais il faut avouer que quand on travaille sur du code legacy ça rend bien service.

Voici donc comment créer et utiliser un mock avec Mockito depuis un test unitaire JUnit.

Il faut tout d'abord ajouter le jar Mockito à votre projet. Si vous utilisez Maven voilà ce que ça donne (attention à bien utiliser le scope test pour éviter que le jar se retrouve en prod):

<dependency>
 <groupId>org.mockito</groupId>
 <artifactId>mockito-core</artifactId>
 <version>1.9.5</version>
 <scope>test</scope>
</dependency>

Ensuite dans votre classe test il faut importer les classes suivantes:

import static org.mockito.Matchers.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

Les imports static permettent d'utiliser directement les méthodes mock, when, ... je trouve que c'est plus lisible.

Ensuite pour créer un mock:

ComplicatedObject legacy = mock(ComplicatedObject.class);

Techniquement on a un mock mais c'est assez limité car toutes les methodes retournent null. Pas pratique pour tester. Il faut donc spécifier le comportement du mock:

when(legacy.complexMethod()).thenReturn("success");

Voilà le mock est prêt à être utilisé. Quand legacy.complexMethod() sera appelé, on récupérera la String "success".

Si la méthode à mocker à des paramètres on peut utiliser tout l'arsenal des Matchers: eq, any, ...

when(legacy.complexMethodWithParam(eq(1))).thenReturn("success");
when(legacy.complexMethodWithParam(eq(0))).thenReturn("failure");

when(legacy.complexMethodWithParam(anyString())).thenReturn("unknown");

Voir les commentaires

Rédigé par Bliz

Publié dans #Java

Repost 0

Publié le 16 Août 2013

Apache fournit une lib HttpClient très simple et qui semble assez fréquemment utilisée. C'est d'ailleurs le cas sur mon projet où elle est utilisée pour interagir avec d'autres services webs.

Pour interroger ces services webs nous utilisons simplement l'authentification HTTP basique soit un login/mot de passe.

Le problème est que par défaut HttpClient n'envoie pas l'authentification dans la requête (pour des raisons de sécurité) ce qui donne le callflow suivant:

Client                                         Server
   | ----- HTTP req sans authentification   ----> |
   | <---- HTTP 401 Authentication required ----- |
   | ----- HTTP req avec authentication     ----> |
   | <---- HTTP 200 OK                      ----- |

Evidemment ça n'est pas optimum et on peut facilement envoyer directement l'authentication dans la première requête comme expiqué par Apache (Preemptive authentication): http://hc.apache.org/httpcomponents-client-ga/tutorial/html/authentication.html

Maintenant voyons comment tester tout ça. Première chose il nous faut un serveur HTTP, ça tombe bien il y en a un dans le JDK (package com.sun.net.httpserver):

HttpServer server = HttpServer.create(new InetSocketAddress(8765), 0);
HttpHandler handler = new HttpHandler() {
   @Override
   public void handle(HttpExchange httpExchange) throws IOException {
      String response = "This is OK";
      httpExchange.sendResponseHeaders(200, response.length());
      OutputStream os = httpExchange.getResponseBody();
      os.write(response.getBytes());
      os.close();
   }   
};
HttpContext context = server.createContext("/test", handler);
context.setAuthenticator(new BasicHttpAuthenticator("realm-test") {
   @Override
   public boolean checkCredentials(String authUser, String authPwd) {
      return "bliz".equals(authUser) && "1234".equals(authPwd);
   }
});
server.setExecutor(null);
server.start(); 

Voilà le serveur est prêt pour notre test. Nous avons:

  • un server HTTP
  • un handler qui sert le chemin "/test"
  • un authenticator qui vérifie les login / mot de passe

C'est pas trop mal mais il va falloir compter les requêtes envoyer au serveur pour vérifier qu'on est bien en mode preemptive et qu'on ne reçoit pas un 401 à chaque fois.

On peut donc imaginer introduire un compteur de requête au niveau du HttpHandler et de l'incrémenter dans la méthode handle().

Oui mais voilà comme il y a un Autenticator associé au context la réponse 401 est renvoyé automatiquement par le serveur sans qu'on s'en aperçoive au niveau du handler.

Il nous faut donc gérer l'authentification directement dans le handler sans l'associer au context. Ce qui donne au niveau du handler:

   @Override
   public void handle(HttpExchange httpExchange) throws IOException {
      requests++;
      String response = "";
      if (checkAuth(httpExchange)) {
         response = "This is OK";
         httpExchange.sendResponseHeaders(200, response.length());
      } else {
         response = "Missing or wrong authentication";
         httpExchange.getResponseHeaders().set("WWW-Authenticate", "Basic realm=realm-test");
         httpExchange.sendResponseHeaders(401, response.length());
      }
      OutputStream os = httpExchange.getResponseBody();
      os.write(response.getBytes());
      os.close();
   }

   boolean checkAuth(HttpExchange httpExchange) {
      Authenticator.Result res = auth.authenticate(httpExchange);
      return res instanceof Authenticator.Success;
   }

Ne pas oublier d'enlever la ligne pour ne pas associer d'authenticator au context:

context.setAuthenticator(...)

et de créer l'authenticator au sein du HttpHandler.

Maintenant on voit passer les 2 requêtes dans notre handler et on peut vérifier facilement les requêtes envoyées.

Voir les commentaires

Rédigé par Bliz

Publié dans #Java

Repost 0

Publié le 13 Août 2013

Lors des tests unitaires j'aime bien afficher les logs d'exécution surtout quand ils deviennent complexe et se rapproche (un peu) des tests fonctionels. Et comme sur mon projet nous utilisons Log4J il suffit d'initialiser Log4J pour activer les logs.

Cela se fait simplement en ajoutant une méthode de 2 lignes annotée avec @BeforeClass dans la classe de test:

   @BeforeClass
   public static void initLog() {
       BasicConfigurator.configure();
       Logger.getRootLogger().setLevel(Level.TRACE);
   } 

Voir les commentaires

Rédigé par Bliz

Publié dans #Java

Repost 0

Publié le 17 Avril 2013

J'ai une application Java que je démarre avec les propriétés qui vont bien pour activer JMX:

-Dcom.sun.management.jmxremote.port=22022' \
-Dcom.sun.management.jmxremote.ssl=false' \
-Dcom.sun.management.jmxremote.authenticate=false 

Mais impossible d'ouvrir une jconsole depuis une machine distante. J'obitent toujours l'erreur suivante:

Connection Failed: Retry?

The connection to <server> did not succeed. Would you like to try again?

Pour creuser l'affaire on peut lancer jconsole avec l'option -debug ce qui ouvre une fenêtre supplémentaire avec les logs.

Et là surprise :

java.rmi.ConnectException: Connection refused to host: 127.0.0.1; nested exception is: 
java.net.ConnectException: Connection refused
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:619)
at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:216)
at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:202)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:128)
at javax.management.remote.rmi.RMIServerImpl_Stub.newClient(Unknown Source)
at javax.management.remote.rmi.RMIConnector.getConnection(RMIConnector.java:2370)
at javax.management.remote.rmi.RMIConnector.connect(RMIConnector.java:296)
at javax.management.remote.rmi.RMIConnector.connect(RMIConnector.java:242)
at sun.tools.jconsole.ProxyClient.tryConnect(ProxyClient.java:352)
at sun.tools.jconsole.ProxyClient.connect(ProxyClient.java:314)
at sun.tools.jconsole.VMPanel$2.run(VMPanel.java:295)
Caused by: java.net.ConnectException: Connection refused
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:391)
at java.net.Socket.connect(Socket.java:579)
at java.net.Socket.connect(Socket.java:528)
at java.net.Socket.<init>(Socket.java:425)
at java.net.Socket.<init>(Socket.java:208)
at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(RMIDirectSocketFactory.java:40)
at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(RMIMasterSocketFactory.java:146)
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:613)
... 10 more

Et oui on essaie de se connecter au 127.0.0.1.

En fait lorsqu'on démarre l'appli sans préciser de server RMI on est par défaut sur localhost. Pour se connecter depuis une machine distante il faut donc utiliser la propriété java.rmi.server.hostname en précisant le nom ou l'adresse IP du serveur sur lequel tourne l'appli:

-Dcom.sun.management.jmxremote.port=22022' \
-Dcom.sun.management.jmxremote.ssl=false' \
-Dcom.sun.management.jmxremote.authenticate=false \
-Djava.rmi.server.hostname=192.168.1.5

Voir les commentaires

Rédigé par Bliz

Publié dans #Java

Repost 0

Publié le 16 Janvier 2013

Voilà un pattern que je rencontre dans notre codebase actuel. Il s'agit d'utiliser une interface java pour y définir des constantes.

Par exemple:

public interface Constantes {

   public static final String MY_NAME = "my_name";
   // etc ...

Ensuite la classe qui utilise ces constantes implémente l'interface:

public class MaClasse implements Constantes {

   private String name = MY_NAME;
   // etc ...

En implémentant l'interface Constantes on peut utiliser directement le nom des constantes sans les préfixer par le nom de l'interface. C'est vrai ça fait plus clair dans le code car on gagne en lisibilité.

Cependant il y a quelques inconvénients qui peuvent faire perdre un temps fou en support (en débuggage quoi).

En fait les constantes sont héritées de l'interface (oui ça fait bizarre pour des champs static mais c'est bien le cas).

De plus si je déclare une constante MY_NAME dans MaClasse je vais masquer celle de l'interface:

public class MaClasse implements Constantes {

   private static final String MY_NAME = "ma_classe";

   private String name = MY_NAME;
   // etc ...

}

Maintenant name vaut "ma_classe" et non plus "my_name" comme défini dans l'interface.

Encore ici on a un lien direct avec l'interface mais ça peut être moins évident avec des sous-classes qui n'implémente pas l'interface directement. (Normalement l'IDE génère un warning en cas de masquage, mais bon).

En fait il existe un moyen plus sûr de faire la même chose. Au lieu d'utiliser une interface il faut mieux utiliser une classe non instanciable, puis utiliser un import static dans la classe qui utilise ces constantes: 

public final class Constantes {

   private Constantes(){
      throw new UnsupportedOperationException();
   }

   public static final String MY_NAME = "my_name";
   // etc ...

Ensuite dans la classe qui utilise les constantes:

import static mon.package.Constantes.*;

public class MaClass {

   private String name = MY_NAME;
   // etc ...

On garde la même lisibilité qu'avec l'interface et les constantes ne sont plus héritées ce qui évite le risque de confusion.

Voir les commentaires

Rédigé par Bliz

Publié dans #Java

Repost 0

Publié le 12 Décembre 2012

Aujourd'hui j'ai besoin de tester un client pour un service web. Et suite au lancement du no-mock movement j'ai décidé de créer un vrai serveur web pour tester mon client.

Bien sûr mon serveur est très basique et ne contient aucune logique métier mais cela reste un vrai serveur HTTP.

La question est de savoir comment créer un vrai serveur en un minimum de ligne. Il existe pas mal de bibliothèque tierce qui permettent d'embarquer le serveur dans l'application java (genre Jetty, ...) mais vu les contraintes, ici je préfère quelque chose de plus simple et qui surtout éviter de rajouter une lib externe pour les tests.

Et bien justement il y a une implémentation d'un serveur HTTP caché au fond du JDK. Il s'agit de la classe "com.sun.net.httpserver.HttpServer".

L'utilisation est assez simple il faut :

  1. Créer une classe qui implémente HttpHandler et qui traitera les requêtes
  2. Créer une instance du HttpServer
  3. Créer un contexte sur le serveur auquel on attribue une instance du HttpHandler défini en 1.
  4. Démarrer le serveur

Voilà un exemple de code qui ne fait qu'envoyer une réponse fixe quelque soit le requête :

package com.over-blog.patatos;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

public class BasicServer {

   private static final String RESPONSE = "<RESPONSE_STRING>";

   static class MyHandler implements HttpHandler {

      public void handle(HttpExchange t) throws IOException {
         t.sendResponseHeaders(200, RESPONSE.length());
         OutputStream os = t.getResponseBody();
         os.write(RESPONSE.getBytes());
         os.close();
      }
   }

   public static void main(String[] args) throws IOException{
      HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
      server.createContext("/", new MyHandler());
      server.setExecutor(null); // creates a default executor
      server.start();
   }

Voir les commentaires

Rédigé par Bliz

Publié dans #Java

Repost 0

Publié le 12 Octobre 2012

Le problème de jour consiste à devoir incrémenter un compteur global qui s'incrémente à chaque transaction.

Il y a bien sûr plusieurs thread qui peuvent générer des transactions simultanément, il faut donc prendre en compte tous les aspects de synchronisation.

ah oui et enfin le compteur doit boucler sur l'intervalle [0-9999].

On peut être tenter d'utiliser synchronized pour controler l'accès au compteur mais les classes Atomic permettent de s'en passer:

static final int MAX = 10000;
final AtomicInteger counter = new AtomicInteger(0);


int getAndIncrementCounter() {
    counter.compareAndSet(Integer.MAX_VALUE, Integer.MAX_VALUE % MAX)
    return counter.incrementAndGet() % MAX;

Voir les commentaires

Rédigé par Bliz

Publié dans #Java

Repost 0