HOAB

History of a bug

Introscope & Unsupported major.minor version 52.0

Rédigé par gorki Aucun commentaire

Problem :

Launching a JVM with Introscope agent on a old JVM 1.7 result in : 

A problem occurred while attempting to create the delegate agent 
[IntroscopeAgent] Agent Unavailable 

Well, as the agent is heavily customized, removing customization was the first step. It starts. 
OK, a few lambda removing alter, I recompiled everything with Java 1.7 target.. And still the same message. 

Solution : 

After a hours ... 

Step 1 : 

  • Decompile
    • com.wily.introscope.agent.AgentShim
  • Add more logs, it confirms this is a class loading problem with major/minor version. I finally get the class name 

Step 2 : 

  • After checking X times my maven settings, I finally used : *
    • javap -v | grep version
    • to check if it was ok or not. 
  • and surprisingly, the generated class was OK ! 
  • but I used also assembly plugin 

Step 3 : 

  • The cause was that I recompile some classes of an old jar and rebuild it 
  • I tracked the faulty classes version in the different repositories  The root cause was that I installed in a local repository the old jar without the modified classes with the command : 
plaintext mvn install:install-file -DcreateChecksum=true -Dfile=./agent/wily/Agent.jar -DgroupId=com.ca.agent -DartifactId=javaagent -Dversion=10.0.7SP3 -Dpackaging=jar -DlocalRepositoryPath=local-maven-repo

which install it also after build in the global repository... 

And assembly plugin use in priority the global repository. 

Well I didn't take time to understand why maven do not use only my local repository for this jar. 

I just add : rm -rf ~/.m2/repository/com/ca/agent/sqlagent in the beginning of my install script  ! 

Well a few hours lost again here...

Springboot + Maven + Junit5

Rédigé par gorki Aucun commentaire

Problème :

Migration de springboot vers  la 2.1.1 et Junit5... et plus rien ne marche, tests introuvables.

Après de multiples tests, ça marchait dans Intellij Idea, mais pas sous maven (MockBean retournait null....)

Solution :

J'ai fait beaucoup d'essais :

  • ajouter junit-platform-commons comme vu ici
  • j'ai suivi les guides de Mkyong Springboot + Maven + Junit5
  • j'ai exclu Junit4 de surefire, ajouter plateform-commons à surefire...

Rien n'y faisait :

  • soit pas de tests
  • soit NullPointerException sur les MockBeans
  • soit "Caused by: java.lang.ClassNotFoundException: org.junit.platform.engine.support.discovery.SelectorResolver"

Au final :

  1. Faire migrer les tests junit vers junit5
    1. Corriger les API
      import org.junit.jupiter.api.Assertions;
      import org.junit.jupiter.api.Test;
    2. Remplacer le SpringRunner
      @ExtendWith({SpringExtension.class})
  2. Simplifier le POM (vu ici : https://github.com/junit-team/junit5/issues/1773)
		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter</artifactId>
			<version>${test.junit.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter-api</artifactId>
			<version>${test.junit.version}</version>
			<scope>test</scope>
		</dependency>
<!--
		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter-engine</artifactId>
			<version>${test.junit.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.junit.platform</groupId>
			<artifactId>junit-platform-commons</artifactId>
			<version>${test.junit.platform}</version>
			<scope>test</scope>
		</dependency>-->

Et bien sur :

		<!-- TEST -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>junit</groupId>
					<artifactId>junit</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

Et enfin :

			<!-- Testing -->
			<plugin>
				<artifactId>maven-surefire-plugin</artifactId>
				<version>2.22.2</version>
			</plugin>

 

Un projet Web avec Hibernate tout simple (avec Maven, déjà ce n'est plus simple...)

Rédigé par gorki Aucun commentaire

Le problème :

Pour faire des tests, il peut être utile d'avoir un projet Hibernate minimaliste, sans fioriture pour tester simplement une fonctionnalité particulière du framework.

Et aussi si ça pouvait être une application WEB, ça serait cool.

Maven c'est pour éviter d'avoir à télécharger les librairies quand on a déjà Eclipse/Maven sur son poste.

On a bien quelques liens sur google, mais je ne trouvais pas ça super simple pour quelqu'un qui connaissait un peu hibernate : ici, google.

Solution :

Librement inspirée de ce site.

Prérequis :

  • savoir ce qu'est Hibernate, Maven, Eclipse, Java, JPA. Je n'explique rien ici, mais le web est là pour ça.
  • avoir un Eclipse récent qui supporte Maven en natif (à partir de la version Kepler je crois)
  • savoir créer un projet sous Eclipse et savoir où se placent les fichiers dans une arbo Maven (Sinon télécharger l'archive en fin de page, je sais GitHub existe...)
  • savoir piocher les bouts qui vont bien pour votre propre besoin, c'est un peu le mode bloc note ici.

.

  1. Créer le pom qui va bien :
    • Servlet pour la partie annotation Web
    • HSQLDB pour la base
    • SLF4J et Logback pour les logs
    • Hibernate pour Hibernate
<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/maven-v4_0_0.xsd">

	<modelVersion>4.0.0</modelVersion>
	<groupId>com.mygroup</groupId>
	<artifactId>testApp</artifactId>
	<packaging>war</packaging>
	<version>1.0-SNAPSHOT</version>

	<name>Webapp</name>

	<dependencies>
		<!-- Servlet -->

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>compile</scope>
		</dependency>

		<!-- BDD -->

		<dependency>
			<groupId>org.hsqldb</groupId>
			<artifactId>hsqldb</artifactId>
			<version>2.3.2</version>
		</dependency>

		<!-- Logger -->

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.6</version>
		</dependency>

		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.1.1</version>
		</dependency>

		<!-- ORM -->

		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>4.3.4.Final</version>
		</dependency>
		


	</dependencies>

	<build>

		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.7</source>
					<target>1.7</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>
  1. Un fichier web.xml tout petit :
    • Attention ! à la version des servlets : 3.0 - pour pouvoir les déployer avec des annotations @WebServlet
    • Un servlet de startup pour initialiser la base mémoire
<web-app 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_3_0.xsd"
	version="3.0">


	<display-name>Test Application</display-name>


	<servlet>
		<servlet-name>StartupGenerique</servlet-name>
		<servlet-class>com.mygroup.servlet.StartupServlet</servlet-class>
		<load-on-startup>0</load-on-startup>
	</servlet>

</web-app>
  1. Une servlet qui répond à toutes les requêtes (on fait dans le super simple)
    • La servlet répond au motif HTTP : "/". Donc toutes les requêtes....
    • Algorithme de haut-niveau :
      • Créer un objet DOG
      • Le sauvegarde
      • Recherche de l'objet DOG sauvé
      • Retourne une réponse
package com.mygroup.servlet;

// Import required java libraries
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mygroup.dao.DogDAO;
import com.mygroup.model.Dog;

/**
 * The Class TestServlet1.
 */
@WebServlet("/")
public class TestServlet extends HttpServlet {

	/** The Constant serialVersionUID. */
	private static final long serialVersionUID = 1L;

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

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		DogDAO dogDAO = new DogDAO();

		dogDAO.startConnection();

		String responseString = null;

		try {
			/*
			 * Save Dog
			 */
			Dog dog = new Dog();
			dog.setName("Beethoven " + System.currentTimeMillis());
			dog.setWeight(45);
			dogDAO.save(dog);

			/*
			 * Find dog
			 */
			dog = dogDAO.find(dog.getId());
			LOGGER.debug("Dog saved : {}", dog.getName());

			/*
			 * Response
			 */
			responseString = "OK, Dog saved : " + dog.getName();
		} catch (Exception e) {
			LOGGER.error("Erreur générale", e);
			responseString = "Erreur, voir log";
		} finally {
			dogDAO.commitConnection();
			dogDAO.closeConnection();
		}

		// Set response content type
		response.setContentType("text/html");
		// Actual logic goes here.
		PrintWriter out = response.getWriter();
		out.println(responseString);
	}
}
  1. Le DAO qui permet de manipuler les objets DOG et d'ouvrir les connexions à la BDD (via DatabaseUtils ci-dessous)
package com.mygroup.dao;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Restrictions;

import com.mygroup.model.Dog;

public class DogDAO {

	private Transaction newTransaction;
	private Session newSession;

	public void startConnection() {
		newSession = DatabaseUtils.getSessionFactory().openSession();
		newTransaction = newSession.beginTransaction();

	}

	public void closeConnection() {
		newSession.close();
	}

	public void commitConnection() {
		newTransaction.commit();
	}

	public void save(Dog dog) {
		newSession.persist(dog);
	}

	public void edit(Dog dog) {
		newSession.merge(dog);
	}

	public Dog find(int dogId) {
		return (Dog) newSession.createCriteria(Dog.class).add(Restrictions.idEq(dogId)).uniqueResult();
	}

	public void remove(Dog dog) {
		newSession.delete(dog);
	}

	public List listALL() {
		return newSession.createQuery("from " + Dog.class).list();
	}
}
  1. Le modèle DOG qui est persisté en base de données (et quand est-ce qu'on pourra se passer de ces getters/setters....)
package com.mygroup.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "dog")
public class Dog {

	public static final String LIST_ALL = "listALL";

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int id;
	private String name;
	private double weight;

	// Getters and Setters
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public double getWeight() {
		return weight;
	}

	public void setWeight(double weight) {
		this.weight = weight;
	}

}
  1. La création de connexion via Hibernate
package com.mygroup.dao;

import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

import com.mygroup.model.Dog;

public class DatabaseUtils {

	private static SessionFactory sessionFactory;

	static {
		Configuration cfg = new Configuration().addAnnotatedClass(Dog.class);
		ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(cfg.getProperties()).build();
		sessionFactory = cfg.buildSessionFactory(serviceRegistry);
	}

	public static SessionFactory getSessionFactory() {
		return sessionFactory;
	}

}
  1. Le fichier hibernate.properties :
hibernate.connection.driver_class = org.hsqldb.jdbcDriver
hibernate.connection.url = jdbc:hsqldb:mem:.
hibernate.connection.username = sa
hibernate.connection.password = 
hibernate.hbm2ddl.auto = update
hibernate.c3p0.min_size=1
hibernate.c3p0.max_size=2
hibernate.c3p0.timeout=5
hibernate.c3p0.max_statements=50
hibernate.dialect = org.hibernate.dialect.HSQLDialect
  1. Le fichier logback pour les logs :
<configuration>

	<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
		<!-- encoders are assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder 
			by default -->
		<encoder>
			<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
			</pattern>
		</encoder>
	</appender>


	<logger name="com.mygroup" level="DEBUG" />


	<root level="error">
		<appender-ref ref="STDOUT" />
	</root>
</configuration>

Et voilà.

Alors attention, ça ne part pas en production comme ça, on est d'accord ! :)

Le zip avec le tout ici : SimpleWebappHibernate.7z

 

 

Fil RSS des articles de ce mot clé