HOAB

History of a bug

H2 base mémoire et client WEB intégré

Rédigé par gorki Aucun commentaire

Le problème :

La base H2 intègre un mode SERVEUR comme Hsqldb ou Derby, mais aussi un mode mixe qui permet au premier client connecté à la base de démarrer un serveur.

Très pratique, en utilisant un client quelconque , on peut se connecter au serveur !

Deux problèmes :

  • quel client ?
  • le mode serveur, OK, mais il ne fonctionne pas avec la base mémoire embarquée !

Solution : quel client ?

Tombé un peu par hasard dessus : ici sur stackoverflow

Il y a une liste ici : http://h2database.com/html/links.html#tools

La console WEB n'est pas intégrée au driver H2, même si dans la librairie H2 il y a la console H2 ; au départ je pensais qu'on aurais pu y accéder en tapant l'URL de la base de données + /console par exemple :)

Pour la console H2 :

  • soit vous avez le raccourci si vous avez installé H2, cf la doc
  • soit via le jar : java org.h2.tools.Console -web -browser
  • sinon, on peut la démarrer tout simplement :
import java.sql.SQLException;
import org.h2.tools.Console;


public class H2Client {

    public static void main(String[] args) {
        try {
            new Console().runTool();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Entrer l'url indiquée dans la configuration ou affichée lors du lancement du serveur pour la base mémoire.

Solution : serveur et base mémoire ?

Idem, dans le même post : ici sur stackoverflow

Sur disque

Ajouter AUTOSERVER=TRUE à la fin de la déclaration de la base de données :

Exemple :

jdbc:h2:~/my-database/db2;AUTO_SERVER=TRUE

En base mémoire

Il faut démarrer le serveur dans la JVM qui utilise la base mémoire embarquée :

Pour la déclaration base mémoire classique :

  • jdbc:h2:mem:test
            Server server = Server.createTcpServer().start();
            System.out.println("URL du serveur H2 : jdbc:h2:" + server.getURL() + "/mem:test");

Notez le "mem:test" à la fin de l'URL, à adapter si vous modifier la votre.

En fin de démarrage de Spring Boot, ça donne :

    @Bean
    public Server initH2Server() {
        try {
            Server server = Server.createTcpServer().start();
            System.out.println("URL: jdbc:h2:" + server.getURL() + "/mem:test");
            return server;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

Jacoco ne génère pas de rapport via Sonar

Rédigé par gorki Aucun commentaire

Le problème :

Configuration du job Jenkins pour Sonar avec les arguments Jacoco :

clean org.jacoco:jacoco-maven-plugin:prepare-agent install -Dmaven.test.failure.ignore=true

Et pourtant pas de couverture de code à la fin de l'exécution du build sonar.

Solution :

Simple quand on connait la réponse, conflit entre  :

  • les arguments dans le pom.xml du plugin Surefire
  • les arguements en ligne de commande du plugin Jacoco.

Du coup, les <argLine> du plugin Surefire dans le pom.xml doivent référencer la variable ${argLine}

 <argLine>-server -ea -XX:MaxPermSize=256m -Xmx4g -XX:-UseSplitVerifier ${argLine}</argLine> 

Extrait de stackoverflow

 

Spring, Autowire, Proxy Class

Rédigé par gorki Aucun commentaire

Le problème :

A l'origine, suite à une réorganisation des classes et un nettoyage des configurations Spring, une des classes (appelons-là ClasseA) n'est plus "Autowired" sur ClasseB.

Alors que :

- les dépendances entre configurations Spring sont OK

- le component scan est OK

- les traces de Spring montrent bien une instanciation de la classe ClasseA et une tentative d'"autowiring" sur la classe ClasseB.

Puis, plus rien, pas de trace d'erreur si ce n'est que l'autowiring indique qu'il n'a pas trouvé de candidat.

 

Solution :

Ce n'est pas très simple.

Première étape :

- le tag qui a fait marcher le test :

<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager" />

Je ne voyais pas le lien entre les transactions spring et ma classe qui n'avait aucun rapport avec les transactions.

En réalité c'est le proxy-target-class="true" qui a fait marcher le test. Grâce à ce lien, il est indiqué que cet attribut s'applique partout !

 

Deuxième étape deux nouveaux tests :

- suppression du proxy-target-class:  le test est KO (revenu à l'état premier)

<tx:annotation-driven transaction-manager="transactionManager" /> 

- ajout d'un tag général dédié aux proxies : le test est KO, il manque cette classe org.aspectj.util.PartialOrder$PartialComparable.

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

<aop:config proxy-target-class="true" />

 

Troisième étape, en ajoutant le jar aspectjweaver (moins lourd que le aspecttools), ça a marché.

Donc c'était bien l'instanciation de ma classe via un JDK Proxy plutôt que via un CGLIB qui a changé la donne.

 

Pour aller plus avant :

- ma classe hérite de org.apache.commons.configuration.CompositeConfiguration

- j'ai essayé d'en extraire une interface pour utiliser les JDK Proxy plutôt que les CGI : échec sur les premiers tests. J'ai laissé filé.

- un mystère que je n'ai pas creusé non plus : pourquoi le fait d'utiliser le proxy-target-class sur le tag aop:config demande plus de fonctionnalités (PartialOrder manquant) que sur tx:annotation-driven ?

- les JDK proxy sont conseillés car ils implique une programmation par interface (pour ceux qui aiment...)

 

Moralité ( il est mort alité...) :

- d'où l'importance de ne pas laisser faire la configuration Spring à n'importe qui, de bien comprendre les tags et leur portées.

- contrôler la portée de vos component-scan, ça aide d'avoir des tests rapides...

 

Mise à jour :

Le problème a aussi une autre conséquence qui bloque le comportement Autowired:

- Si une classe CLASSE_REELLE (@Component ) implémente une interface INTERFACE_GENIALE

- Une classe B (de test par exemple) essaye d'injecter A directement

- aop:config n'est pas configuré pour empêcher l'utilisation des proxy Java ! Spring décide par défaut d'utiliser les proxy dès qu'une interface est présente. (cf ce lien :  JDK- and CGLIB-based proxies)

Conséquence : Spring ne qu'injecter des variables de types INTERFACE_GENIALE et pas de type CLASSE_REELLE. Seules les interfaces sont injectables.

 

Spring Boot et CXF

Rédigé par gorki Aucun commentaire

Le problème :

SpringBoot c'est assez pratique, le REST est géré de base avec Spring Boot MVC, mais parfois on a aussi besoin d'un serveur SOAP et là souvent CXF est utilisé plutôt que Spring MVC...)

Petit pense bête car les solutions trouvées sur le net me semble :

- compliquée : en utilisant Spring integration

- incomplète : déclaration simple

Solution :

En trois étapes :

  1. - Utiliser un fichier spring externe à Spring Boot
  2. - Déclarer la servlet CXF
  3. - Configurer Spring et CXF

Point 1 et 2 : dans l'application Spring Boot

Attention :
- dans notre exemple, CXF va gérer les URLs commençant par /soap/
- ne pas oublier de démarrer la servlet CXF

package com.test.springboot.cxf

import javax.annotation.PreDestroy;

import org.apache.cxf.transport.servlet.CXFServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

/**
 *
 */
@Configuration
@ImportResource("classpath:spring-cxf.xml")
@ComponentScan
@EnableAutoConfiguration()
public class SpringBootCxf {
    private static final Logger LOGGER = LoggerFactory.getLogger(SpringBootCxf.class);

    public static void main(final String[] args) throws Exception {
        SpringApplication.run(SpringBootCxf.class, args);
    }

    @Bean
    public ServletRegistrationBean cxfServlet() {
        ServletRegistrationBean servletDef = new ServletRegistrationBean(new CXFServlet(), "/soap/*");
        servletDef.setLoadOnStartup(1);
        return servletDef;
    }

    @PreDestroy
    public void exit() {
        LOGGER.info("Exiting");
    }
}

Point 3 : Le fichier de configuration Spring - CXF

Attention :
- le point important ici est le lien entre le fichier XML et le composant qui implémente le service :

implementor="#eventServiceSoap"

Fichier XML complet :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws"
	xmlns:soap="http://cxf.apache.org/bindings/soap"
	xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
            http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

	<!-- these are included in the dependency jar -->
	<import resource="classpath:META-INF/cxf/cxf.xml" />
	<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

	<!-- event -->
	<jaxws:endpoint id="eventServiceEndpoint" implementor="#eventServiceSoap"
		address="/event">
		<jaxws:properties>
			<entry key="schema-validation-enabled" value="false" />
		</jaxws:properties>
		<jaxws:binding>
			<soap:soapBinding version="1.2" mtomEnabled="true" />
		</jaxws:binding>
	</jaxws:endpoint>	
</beans>

Ensuite on peut utiliser un composant "Autowired" by Spring :
- EventManagerBean est le résultat de la génération WSDL2Java

@Component("eventServiceSoap")
public class EventServiceImpl implements EventManagerBean {

    /** The Constant LOGGER. */
    private static final Logger LOGGER = LoggerFactory.getLogger(EventServiceImpl.class);

    @Autowired
    EventRepository eventRepository;

    ...

 

 

 

 

Git, les branches et pourquoi mes fichiers ne disparaissent pas

Rédigé par gorki Aucun commentaire

Le problème :

C'est tout bête, c'est expliqué plein de fois partout, mais comme je l'ai compris un peu sur le tard...

Pourquoi quand je change de branche GIT, mais fichiers ne sont pas uniquement ceux de la branche sur lequel je viens de switcher ?

 

Solution :

Tout simplement parce que les fichiers étaient soit dans la working directory, soit dans le stage.(index)

En fait le checkout ne met à jour que les fichiers déjà commités dans les branches et ne touche pas aux fichiers stage (index) ou dans le working directory.

Autrement dit tant que vos fichiers ne sont pas commités, il resteront présent (sauf si vous les supprimer, clean la branche, etc...)

Et autre chose importante : vous pouvez modifier tout ce que vous voulez, changer de branche et commiter dans cette branche sans impacter les autres (conséquences du point précédent). Ce qui permet de commencer un travail, de se dire "mince ! je devais faire une branche, checkout d'une nouvelle branche et commit"

Liens utiles :

- ce que j'aurais aimé savoir avant de commencer GIT. Cela sous entend de prendre du temps avant de commencer à jouer avec GIT.

- les différents espace GIT et comment faire passer les fichiers de l'un à l'autre

- les différents espace GIT en plus détaillé

Fil RSS des articles