RSS Feed

Archives de la catégorie ‘Java EE’

Vagrant – JDK Oracle sur Ubuntu

16 September 2014 par SeB Pas de commentaire »

Récemment, j’ai voulu créer une box Vagrant contenant un environnement de développement Java prêt à l’usage. Sur GNU/Linux, il existe deux possibilités pour le JDK :

Souhaitant rester au plus proche de la version de production, j’ai décidé d’installer la version d’Oracle. Cette version n’est pas disponible sur les dépôts officiels d’Ubuntu. Il faut passer par les dépôts PPA.

Création de la VM

Déclaration

La déclaration de la box est assez simple avec le fichier VagrantFile :

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/trusty32"
  config.vm.provision :shell, :path => "scripts/provision_java.sh"
end

Provisionning

Comme je n’ai pas encore eu le temps de me pencher sur Puppet ou Chef, l’installation automatique du JDK 8 d’Oracle est réalisée au travers d’un simple script shell qui se charge de réaliser les tâches suivantes :

  1. vérifie si le provisionnement de Java a déjà été fait
  2. configure les dépôts APT les plus rapides
  3. ajoute le dépôt PPA
  4. lance l’installation automatique de Java 8
  5. configure la variable d’environnement JAVA_HOME
  6. nettoie le système (utile en cas d’échec de l’installation)

Voici le contenu du fichier scripts/provision_java.sh :

echo "[vagrant provisioning] Checking if the box was already provisioned..."
if [ -e "/home/vagrant/.provision_java_check" ]
then
  echo "[vagrant provisioning] The box is already provisioned..."
  exit
fi
echo "[vagrant provisioning] Updating mirrors in sources.list"
sudo sed -i -e '1ideb mirror://mirrors.ubuntu.com/mirrors.txt trusty main restricted universe multiverse\ndeb mirror://mirrors.ubuntu.com/mirrors.txt trusty-updates main restricted universe multiverse\ndeb mirror://mirrors.ubuntu.com/mirrors.txt trusty-backports main restricted universe multiverse\ndeb mirror://mirrors.ubuntu.com/mirrors.txt trusty-security main restricted universe multiverse\n' /etc/apt/sources.list
sudo apt-get update
echo "[vagrant provisioning] Installing Java..."
sudo apt-get -y install python-software-properties
sudo add-apt-repository -y ppa:webupd8team/java
sudo apt-get update
echo oracle-java8-installer shared/accepted-oracle-license-v1-1 select true | sudo /usr/bin/debconf-set-selections
echo oracle-java8-installer shared/accepted-oracle-license-v1-1 seen true | sudo /usr/bin/debconf-set-selections
sudo apt-get -y install oracle-java8-set-default
export JAVA_HOME="/usr/lib/jvm/java-8-oracle/jre"
echo "[vagrant provisioning] Java installed"
sudo dpkg --configure -a
apt-get autoremove -y
echo "[vagrant provisioning] Creating .provision_java_check file..."
touch .provision_java_check

Avec ces deux fichiers, vous pouvez lancer les commandes suivantes :

$ vagrant up
$ vagrant ssh

Une fois la VM créée et une fois connecté sur celle-ci, vous pouvez vérifier que le Java est correctement installé :

$ echo $JAVA_HOME
/usr/lib/jvm/java-8-oracle

$ java -version
java version "1.8.0_20"
Java(TM) SE Runtime Environment (build 1.8.0_20-b26)
Java HotSpot(TM) Client VM (build 25.20-b23, mixed mode)

Votre environnement de développement Java 8 est prêt !

Packaging de la box

Vous avez pu remarquer que le provisionnement de cette VM a pris un certain temps (une dizaine de minutes lors de mon test) entre la mise à jour des dépôts APT et le téléchargement du Java 8. Afin que la création des prochaines VM soit plus rapide, vous pouvez packager celle-ci. Dans un premier temps, il faut effacer l’empreinte mémoire pour réduire au maximum la taille de la box avant de l’arrêter :

$ sudo dd if=/dev/zero of=/EMPTY bs=1M
$ sudo rm -f /EMPTY
$ sudo shutdown -h now

Il existe des articles qui expliquent une ou deux méthodes pour réduire significativement la taille d’une box Vagrant.

Ensuite, il faut créer la box avec la commande suivante :

$ vagrant package --output trusty32-java8-oracle.box

Installation de la box

Cette dernière étape est très simple et s’effectue avec la commande :

$ vagrant box add trusty32/java8/oracle trusty32-java8-oracle.box

Réutilisation de la box

Pour réutiliser cette box dans de nouvelles VM Vagrant, il suffit d’exécuter les commandes suivantes :

$ vagrant init trusty32/java8/oracle
$ vagrant up

Ou bien de créer le fichier VagrantFile suivant :

Vagrant.configure("2") do |config|
  config.vm.box = "trusty32/java8/oracle"
end

Cette fois, la création et le démarrage de la VM a pris moins de 3 minutes !

Vous êtes maintenant équipé pour créer à la demande des environnements de développement Java 8 très très rapidement.

 

Devoxx France 2014, c’est (déjà) fini !

22 April 2014 par SeB 2 commentaires »

Comme d’habitude, Devoxx France est l’événement dans l’hexagone que les développeurs Java ne doivent pas manquer.

Voici les conférences auxquelles j’ai pu assister :

Je vais maintenant digérer toutes ces informations. Vous devriez retrouver certains de ces sujets bientôt sur ces pages.

Mon grand regret ? Ne pas avoir le don de l’ubiquité ! En effet, le choix des sessions n’est pas toujours facile… Heureusement, Parleys viendra à mon secours. ;-)

Pour conclure, c’était 3 jours intensifs, très bien organisés et qui donnent envie d’être présents à la prochaine édition au Palais des Congrès ! Un grand bravo aux organisateurs, à l’équipe, aux sponsors, aux hôtes, aux speakers et aux participants !

 

Eclipse JavaDoc – génération automatique sur un Java bean

14 April 2014 par SeB 2 commentaires »

Il n’y a pas que les adeptes de Lombok qui trouvent que la création d’un Java bean est une tâche répétitive et non gratifiante. Et que dire de la JavaDoc de ces derniers ? Ne vous est-il jamais arrivé de devoir ajouter la JavaDoc sur un Java bean avec plus d’une dizaine d’attributs ? Et donc 2 fois plus de méthodes !

Vous avez peut-être commencé à ajouter manuellement chaque bloc de JavaDoc attribut par attribut, méthode par méthode… Stop ! Il est possible d’automatiser cette tâche en lançant une génération sur tous les attributs et toutes les méthodes.

Imaginons la classe Java suivante :

package com.mycompany.myproject.mymodule;

import java.util.Date;

public class MyEntity {
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public Date getCreateDate() {
		return createDate;
	}
	public void setCreateDate(Date createDate) {
		this.createDate = createDate;
	}
	public int getNumberOfCounters() {
		return numberOfCounters;
	}
	public void setNumberOfCounters(int numberOfCounters) {
		this.numberOfCounters = numberOfCounters;
	}
	private long id;
	private Date createDate;
	private int numberOfCounters;
}

Rendez-vous dans l’onglet “Project Explorer” ou “Outline” et sélectionnez tous les éléments pour lesquels il faut générer la JavaDoc :

project_exploreroutline

Faites le raccourci clavier “Alt+ Shift + J” et observez le résultat :

package com.mycompany.myproject.mymodule;

import java.util.Date;

public class MyEntity {
	/**
	 * @return
	 */
	public long getId() {
		return id;
	}
	/**
	 * @param id
	 */
	public void setId(long id) {
		this.id = id;
	}
	/**
	 * @return
	 */
	public Date getCreateDate() {
		return createDate;
	}
	/**
	 * @param createDate
	 */
	public void setCreateDate(Date createDate) {
		this.createDate = createDate;
	}
	/**
	 * @return
	 */
	public int getNumberOfCounters() {
		return numberOfCounters;
	}
	/**
	 * @param numberOfCounters
	 */
	public void setNumberOfCounters(int numberOfCounters) {
		this.numberOfCounters = numberOfCounters;
	}
	/**
	 *
	 */
	private long id;
	/**
	 *
	 */
	private Date createDate;
	/**
	 *
	 */
	private int numberOfCounters;
}

La JavaDoc a été générée. Mais il faut avouer que de la JavaDoc vide ne fait pas très sérieux… C’est ici qu’entre en scène JAutodoc. Ce plugin pour Eclipse se charge d’ajouter automatiquement la JavaDoc dans les fichiers source. Pour cela, il faut faire un clic droit sur votre classe Java (ou sur un package pour appliquer la génération à toutes les classes), puis choisir “JAutodoc -> Add Javadoc”.

before_jautodoc

La JavaDoc générée automatiquement ressemble maintenant à quelque chose :

package com.mycompany.myproject.mymodule;

import java.util.Date;

// TODO: Auto-generated Javadoc
/**
 * The Class MyEntity.
 */
public class MyEntity {

	/**
	 * Gets the id.
	 *
	 * @return the id
	 */
	public long getId() {
		return id;
	}

	/**
	 * Sets the id.
	 *
	 * @param id the new id
	 */
	public void setId(long id) {
		this.id = id;
	}

	/**
	 * Gets the creates the date.
	 *
	 * @return the creates the date
	 */
	public Date getCreateDate() {
		return createDate;
	}

	/**
	 * Sets the creates the date.
	 *
	 * @param createDate the new creates the date
	 */
	public void setCreateDate(Date createDate) {
		this.createDate = createDate;
	}

	/**
	 * Gets the number of counters.
	 *
	 * @return the number of counters
	 */
	public int getNumberOfCounters() {
		return numberOfCounters;
	}

	/**
	 * Sets the number of counters.
	 *
	 * @param numberOfCounters the new number of counters
	 */
	public void setNumberOfCounters(int numberOfCounters) {
		this.numberOfCounters = numberOfCounters;
	}

	/** The id. */
	private long id;

	/** The create date. */
	private Date createDate;

	/** The number of counters. */
	private int numberOfCounters;
}

Remarquez que non seulement la JavaDoc n’est plus vide mais en plus JAutodoc utilise le CamelCase pour formuler une documentation pour naturelle.

Ce plugin offre de nombreuses autres possibilités que je n’ai pas encore exploré et que je vous laisse découvrir. En 5 minutes de lecture, cet article va soit vous en faire gagner des dizaines si ce n’est pas plus, soit vous faire ajouter de la JavaDoc là où elle faisait défaut…

 

Il était une fois le Singleton en Java

28 October 2013 par SeB 6 commentaires »

Parmi tous les Design Patterns, le plus connu est sûrement le Singleton. Malheureusement, son implémentation n’est pas aussi triviale qu’il n’y parait en Java. Voici donc quelques exemples d’implémentation…

Eager initialization

Si l’application a toujours besoin de cette instance et que sa création ne mobilise pas trop de ressource, il est acceptable de l’écrire ainsi :

public class EagerSingleton {
  private static final EagerSingleton instance = new EagerSingleton();

  private EagerSingleton() {}

  public static EagerSingleton getInstance() {
    return instance;
  }
}

Cependant, il est souvent nécessaire de retarder le chargement de l’instance. De plus, lors de l’implémentation d’un composant réutilisable, il ne faut pas imposer le chargement de tous les singletons à l’application utilisatrice si elle n’en a pas besoin !

Lazy initialization

C’est pourquoi la première implémentation réfléchie mais naïve qui vient à l’esprit est la suivante :

public class LazyInitializationSingleton {
  private static LazyInitializationSingleton instance;

  private LazyInitializationSingleton(){}

  public static LazyInitializationSingleton getInstance() {
    if (instance==null) {
      instance = new LazyInitializationSingleton();
    }
    return instance;
  }
}

Elle semble juste. Cependant, elle n’est pas thread-safe. Dans un contexte multi-thread, plusieurs instances peuvent être créées.

Double check thread-safe

Une fois que la problématique multi-thread est prise en compte, le développeur est tenté de l’implémenter de cette manière :

public class ThreadSafeSingleton {
  private static ThreadSafeSingleton instance;

  private ThreadSafeSingleton() {}

  public static ThreadSafeSingleton getInstance() {
    if (instance==null) {
      synchronized (ThreadSafeSingleton.class) {
        if (instance==null) {
          instance = new ThreadSafeSingleton();
        }
      }
    }
    return instance;
  }
}

Cette implémentation donne l’impression de fonctionner. Or selon, le compilateur, la JVM ou l’OS, le comportement peut différer. A tel point que le “double check locking” est considéré comme un antipattern. Java 5.0 étend la sémantique du mot-clé volatile pour garantir le “flush” des caches avant l’usage de la variable. En utilisant cette solution, vous acceptez d’avoir un code moins performant.

L’énumération

Dans la seconde édition d’Effective Java, Joshua Bloch propose d’utiliser les enum pour implémenter un singleton :

public enum EnumSingleton {
  INSTANCE;

  public static void doStuff() {
    // do stuff
  }
}

Cette solution est terriblement simple mais possède un inconvénient majeur : les énumérations sont moins flexibles que les classes (héritage, sérialisation, etc…).

Bill Pugh Singleton

Cette implémentation tente de réconcilier toutes précédentes au moyen d’une classe imbriquée :

public class BillPughSingleton {
  private BillPughSingleton() {}

  static private class SingletonHelper {
    private static final BillPughSingleton INSTANCE = new BillPughSingleton();
  }

  public static BillPughSingleton getInstance() {
    return SingletonHelper.INSTANCE;
  }
}

Elle reprend le principe de la Eager initialization sans avoir l’inconvénient identifié sur le lazy-loading puisque la classe imbriquée n’est connue de personne. Enfin, cette implémentation est thread-safe sans avoir à recourir à des mots-clés du langage tels que synchronized, volatile ou enum.

Avant de terminer, sachez que si vous utilisez des frameworks d’injection de dépendances tel que Spring Framework, il est possible de le laisser gérer vos classes comme des singletons.

Pourquoi vous avoir présenté toutes ces implémentations ? Simplement pour montrer que l’implémentation d’un Design Pattern, aussi simple soit-il, n’est pas aussi triviale qu’il n’y parait. Et la première question à se poser est : “Pourquoi utilise-t-on ce modèle de conception ?”. Il est également recommandé de bien documenter l’implémentation afin d’expliciter le contexte d’utilisation. ;)

Histoire à suivre…

 

Spring MVC Test – tester vos MessageConverter personnalisés

7 August 2013 par SeB Pas de commentaire »

Précédemment, nous avons vu comment ajouter le support du format CSV dans Spring MVC au moyen des MessageConverter. Il est maintenant temps d’automatiser les tests de ce composant. Pour cela, il faut utiliser Spring MVC Test framework.

Dans un premier temps, il faut déclarer un context Spring simplifié pour le test dans un fichier test-web-context.xml :

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:mvc="http://www.springframework.org/schema/mvc"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
  <context:component-scan base-package="com.mycompany.myproject.web.mvc.controler" />
</beans:beans>

Ensuite, il faut écrire un test unitaire qui va :

  • configurer le MessageConverter à tester
  • charger le contrôleur à tester
  • mocker les services du contrôleur
  • tester le comportement des requêtes proposées par le contrôleur

Voici le code du test unitaire utilisant Spring Test ainsi que Mockito :

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:test-web-context.xml" })
@WebAppConfiguration
public class StatsControllerTest {
  private static final String CSV_TEST_FILE = "src/test/resources/test.csv";

  @Autowired
  private StatsController statsController;

  @Autowired
  private CsvMessageConverter csvMessageConverter;

  @Autowired
  private StatsService statsService;

  private MockMvc mockMvc;

  private String csvContent;

  @Before
  public void setUp() {
    // charge le mock MVC et configure le CsvMessageConverter à tester
    mockMvc = MockMvcBuilders.standaloneSetup(statsController)
      .setMessageConverters(csvMessageConverter).build();

    // "mocke" le service utilisé par le StatsController à tester
    CsvReader reader = new CsvReader(new FileReader(CSV_TEST_FILE));
    List records = reader.readAllRecords();
    when(statsService.findAllStats(anyInt(), anyInt())).thenReturn(records);

    // charge le jeu de test
    csvContent = new String(Files.readAllBytes(Paths.get(CSV_TEST_FILE)));
  }

  @Test
  public void testGetAllStats() throws Exception {
    // exécute la requête HTTP GET, affiche des traces dans la console et vérifie le retour
    mockMvc.perform(get("/stats/2013/08.csv")).andDo(print())
      .andExpect(status().isOk())
      .andExpect(content().contentType("text/csv;charset=UTF-8"))
      .andExpect(content().string(csvContent));
    verify(statsService).findAllStats(2013, 8);
 }

  @Test
  public void testSaveAllStats() throws Exception {
    // exécute la requête HTTP POST, affiche des traces dans la console et vérifie le retour
    mockMvc.perform(
      post("/stats/2013/08").contentType(
        new MediaType("text", "csv", Charset.forName("utf-8")))
        .content(csvContent)).andDo(print())
      .andExpect(status().isOk())
      .andExpect(content().string(csvContent));
    verify(statsService).saveAllStats(2013, 8, Mockito.anyList());
    }

  @Configuration
  @EnableWebMvc
  public static class TestConfiguration {
    @Bean
    public StatsService statsService() {
      return Mockito.mock(StatsService.class);
    }
  }
}

Comme vous pouvez le constater, la mise en place du test est assez simple. Notez que des importations statiques ont été réalisées afin d’utiliser la fluent API :

import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

Même si le prétexte de l’article est de donner la méthode pour tester un MessageConverter, vous avez ici toutes les informations de base pour réaliser les tests unitaires de vos contrôleurs implémentés avec Spring MVC.

Vous n’aurez maintenant plus d’excuse pour ne pas avoir automatisé les tests unitaires de vos contrôleurs (de la couche MVC) ! :P

 

Spring MVC – support du CSV

6 August 2013 par SeB Pas de commentaire »

S’il y a quelque chose que j’apprécie particulièrement dans Spring MVC, c’est bien la facilité et la rapidité à mettre en place des API REST. Par défaut, Spring MVC propose le support du XML, JSON, RSS, Atom et images.

L’ajout du support d’un nouveau format est extrêmement simple. Pour cela, il suffi d’implémenter l’interface AbstractHttpMessageConverter. Voici ci-dessous l’implémentation pour le support des messages HTTP au format CSV :

@Component
public class CsvMessageConverter extends AbstractHttpMessageConverter<List<?>> {
  public static final MediaType MEDIA_TYPE = new MediaType("text", "csv", Charset.forName("utf-8"));

  public CsvMessageConverter() {
    super(MEDIA_TYPE);
  }

  @Override
  protected boolean supports(Class<?> clazz) {
    return List.class.isAssignableFrom(clazz);
  }

  @Override
  protected void writeInternal(List<?> response, HttpOutputMessage output)
      throws IOException, HttpMessageNotWritableException {
    OutputStream out = output.getBody();
    CsvWriter writer = new CsvWriter(new OutputStreamWriter(out));
    writer.writeAllRecords(reponse);
    writer.flush();
    writer.close();
  }

  @Override
  protected List<?> readInternal(Class<? extends List<?>> request,
      HttpInputMessage input) throws IOException,
      HttpMessageNotReadableException {
    InputStream in = input.getBody();
    CsvReader reader = new CsvReader(new InputStreamReader(in));
    List<?> records = reader.readAllRecords();

    return records;
  }
}

Une fois le convertisseur implémenté, il faut le déclarer dans la configuration de Spring MVC :

<mvc:annotation-driven>
  <mvc:message-converters>
    <beans:bean class="com.mycompany.myproject.http.converter.CsvMessageConverter" />
  </mvc:message-converters>
</mvc:annotation-driven>

Maintenant, votre application est prête à générer ou recevoir des requêtes HTTP au format CSV très simplement comme dans le contrôleur suivant :

@Controller
public class StatsController {
  @Autowired
  private StatsService statsService;

  @RequestMapping(
    value = "/stats/{year}/{month}.csv",
    method = RequestMethod.GET,
    produces = "text/csv")
  @ResponseBody
  public List<Stats> getAllStats(@PathVariable("year") int year,
      @PathVariable("month") int month) throws IOException {
    return statsService.findAllStats(year, month);
  }

  @RequestMapping(
    value = "/stats/{year}/{month}",
    method = RequestMethod.POST,
    consumes = "text/csv")
  public List<Stats> saveAllStats(@PathVariable("year") int year,
      @PathVariable("month") int month, @RequestBody List<Stats> stats) {
    statsService.saveAllStats(year, month, stats);

    return statsService.findAllStats(year, month);
  }
}

Si vous souhaitez aller plus loin, vous pouvez bien-entendu consulter la JavaDoc de HttpMessageConverter.

Vous êtes donc maintenant capable de proposer le téléchargement de données au format CSV très simplement dans votre application. Libre à vous de reprendre ce convertisseur ou d’en implémenter d’autres. Vous avez peut-être besoin d’un convertisseur Protobuf ? :-P

 

Validation XML avec des schémas inclus/importés

25 July 2013 par SeB Pas de commentaire »

La validation d’un fichier XML avec un fichier de schéma XML indépendant est assez simple en Java. En revanche, dès que le schéma XML importe ou inclut d’autres schémas, la validation échoue car ces fichiers se sont pas chargés automatiquement. Après investigation, l’API de validation XML ne peut pas charger les schémas inclus. Heureusement, cette API permet d’enregistrer son propre résolveur afin de fournir le contenu des XSD incluses/importées.

Pour celà, il faut implémenter l’interface “LSInput” qui se charge de représenter le contenu du schéma chargé :

public class LocalInput implements LSInput {
	private String publicId;
	private String systemId;
	private BufferedInputStream inputStream;

	public LocalInput(String publicId, String sysId, InputStream input) {
		this.publicId = publicId;
		this.systemId = sysId;
		this.inputStream = new BufferedInputStream(input);
	}

	@Override
	public String getPublicId() {
		return publicId;
	}

	@Override
	public void setPublicId(String publicId) {
		this.publicId = publicId;
	}

	@Override
	public String getBaseURI() {return null;}

	@Override
	public InputStream getByteStream() {return null;}

	@Override
	public boolean getCertifiedText() {return false;}

	@Override
	public Reader getCharacterStream() {return null;}

	@Override
	public String getEncoding() {return null;}

	@Override
	public String getStringData() {
		synchronized (inputStream) {
			try {
				byte[] input = new byte[inputStream.available()];
				return new String(inputStream.read(input));
			} catch (IOException e) {
				return null;
			}
		}
	}

	@Override
	public void setBaseURI(String baseURI) {}

	@Override
	public void setByteStream(InputStream byteStream) {}

	@Override
	public void setCertifiedText(boolean certifiedText) {}

	@Override
	public void setCharacterStream(Reader characterStream) {}

	@Override
	public void setEncoding(String encoding) {}

	@Override
	public void setStringData(String stringData) {}

	@Override
	public String getSystemId() {
		return systemId;
	}

	@Override
	public void setSystemId(String systemId) {
		this.systemId = systemId;
	}

	public BufferedInputStream getInputStream() {
		return inputStream;
	}

	public void setInputStream(BufferedInputStream inputStream) {
		this.inputStream = inputStream;
	}
}

Ensuite, il faut implémenter le résolveur qui se charge de résoudre les dépendances avec des chemins relatifs par rapport à la XSD d’origine :

public class LocalResourceResolver implements LSResourceResolver {
	private final String relativePath;

	public LocalResourceResolver(final String relativePath) {
		this.relativePath = relativePath;
	}

	@Override
	public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) {
		String xsdFilePath = relativePath != null ? (relativePath + "/" + systemId) : systemId;
		InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(xsdFilePath);
		LSInput input = new LocalInput(publicId, systemId, resourceAsStream);
		input.setBaseURI(baseURI);

		return input;
	}
}

Enfin, il faut enregistrer ce résolveur auprès de la fabrique de schéma :

String xsdSourcePath;//chemin où se trouve la XSD afin de résoudre les chemins relatifs
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
factory.setResourceResolver(new LocalResourceResolver(xsdSourcePath));

Cette solution se base sur l’article XML validation with imported/included schemas de Nicolas Fränkel et ce resource resolver pour la résolution des chemins relatifs.

Avec ces deux classes, vous serez capable de valider vos fichiers XML avec des XSD factorisées (ie. utilisant les tags <xs:import /> ou <xs:include />). Bonne validation ! :-)

 

Android Devcast – le podcast Android en français

22 May 2012 par SeB Pas de commentaire »

Même si je ne suis pas un habitué des podcasts, je vous avais déjà présenté Les Cast Codeurs. Comme le titre de ce billet le laisse deviner, aujourd’hui je souhaite pour informer qu’un podcast dédié à Android ainsi qu’à sa communauté francophone existe depuis quelques mois. Ce petit nouveau se nomme l’Android Devcast.

C’est encore très très jeune mais on ne peut que saluer l’initiative et leur souhaiter une longue existence !

 

NormandyJUG – annotations

23 January 2012 par SeB Pas de commentaire »

Mardi dernier, j’ai assisté lors de la session du NormandyJUG à la présentation d’Olivier Croisier sur les annotations. Autant le dire tout de suite : je ne suis pas fan des annotations. J’ai beaucoup de mal avec cette mode de mettre des éléments de configuration dans du code source. On nous rabâche depuis des années que le code source doit être réutilisable et donc générique. Or que depuis l’apparition des annotations, je vois beaucoup de code contenant du paramétrage. On se retrouve donc avec des binaires liés à un environnement. :-(

Néanmoins, c’est sans apriori que je me suis rendu à cette session. Espérant, apprendre quelque chose et peut-être changer d’avis. ;-)

Introduction

En regardant en arrière, on se rend compte qu’XDoclet est l’ancêtre des annotations. Ce système utilisait déjà une méthode de paramétrage et/ou injection/génération de code à partir d'”@” dans la JavaDoc. Cette implémentation avait deux défauts :

  • tout n’est pas documentable dans le langage Java
  • la documentation disparait à la compilation

Voilà pourquoi la création des annotation était nécessaire.

Afin d’assurer une compatibilité du byte code avec le Java <=4, l’implémentation des annotations réutilise les concepts existants dans la langage d’une façon parfois déroutante…

Annotations personnalisées

Pourquoi ?

  • remplacer les fichiers de configuration
  • simplifier le code avec la meta-programmation
  • ajouter des règles de compilation

Caractéristiques

  • un champ d’action
  • une durée de vie (source, compilation, exécution)

La compilation

Les annotations permettent de réaliser un plugin pour le compilateur (Java >=6) via les Pluggable Annotation Processor. Ceci permet entre autre de casser le build. Ce qui peut de révéler très pratique pour faire respecter des best pratices de développement. ;-)

Pourquoi ?

  • La génération de ressources
    • La configuration
    • Les classes
    • La documentation
  • L’amélioration du compilateur
    • Norme de codage
    • Message d’erreur

A ce stade de la présentation, Olivier met en garde l’utilisation de certain framework tel de Lombok qui a quelques contraintes tel que la dépendance de la compilation. En effet, votre compilateur doit être supporté par le projet. Comme toujours, c’est à vous d’estimer le ratio entre risques et avantages.

Comment ?

Ces annotations sont implémentées via AbstractProcessor. Une API permet de faire de l’introspection et découvrir dynamiquement les annotations. Nous avons le droit à une petite démo avec le  SerializableClassesProcessor.

Le runtime

Pourquoi ?

  • Le mapping
  • Les POJO
  • La configuration et/ou les frameworks

Notre intervenant nous illustre cela avec un CSVReader.

Aller plus loin…

Pour nous montrer ce qu’il est possible de faire avec les annotations, notre invité nous présente alors son proof of concept d’injection d’annotations : AnnotationInjector. Il nous avoue que c’est un framework assez sympa à réaliser mais qu’il n’a pas encore trouvé son utilité ! :-D

La conclusion

Une session très enrichissante qui ma réconcilié avec les annotations ! J’éviterai toujours les éléments de configuration dans le code (surtout s’il est lié à un environnement tel que développement, recette ou production). Néanmoins, j’ai déjà testé les Annotations Processor. Et les sembles prometteuses. :-)

La session se termine par une présentation de seren. Cet outil, réalisé par Olivier Crosier, a pour objectif d’optimiser les temps de traitement de la sérialisation et de désérialisation.

Si ce compte-rendu vous a donné envie de vous mettre aux annotations, sachez que la présentation et le code source est disponible sur GitHub.

 

NormandyJUG – Hibernate vs Cloud Computing & NoSQL

14 December 2011 par SeB Pas de commentaire »

Après plus d’un an et demi de pause forcée, j’ai pu enfin retourner au NormandyJUG !

Cette soirée était consacrée à un seul et unique thème :  l’accès aux données face à la montée en charge. La session était animée par Julien Dubois qui est entre autre l’auteur de Spring par la pratique. Le sujet ou l’intervenant devaient intéresser puisque nous étions une 50aine.

En partant du principe que la scalabilité d’une application Java est limitée par la base de données, la présentation consistait à évaluer les solutions disponibles. En effet, avec le Cloud Computing, il est “facile” d’ajouter des machines pour assurer la montée en charge d’une application mais le point de contention reste le serveur de base de données. Avant d’aller plus loin, il faut rappeler le théorème de CAP qui indique qu’il est impossible de répondre à ces trois contraintes en même temps :

  • Cohérent (Consistent)
  • Disponible (Available)
  • Tolérant aux pannes réseaux (Partition tolerant)
Quelles sont donc les solutions pour la base de données ne soit plus un point de contention ?

Le serveur de base de données

Les éditeurs de base de données proposent par exemple du partitionning de table sur différents disques. Les lectures seront optimisées mais ça ne résout pas réellement le problème d’écriture dans les faits.
Il est également possible de mettre en place des clusters d’applications pour le serveur de base de données. A priori, la montée en charge est possible. Cependant, le cluster doit positionner les locks réseaux qui malheureusement plombent les performances.

Hibernate et le cache

S’il n’est pas possible d’améliorer à volonté la performance de la base de données, nous pourrions être tenté de ne plus systématiquement faire appel à cette dernière. C’est là qu’en en scène le cache de niveau 2 d’Hibernate.
Il faut connaitre le fonctionnement d’Hibernate pour bien utiliser le cache. Il est également possible de mettre en pratique la loi de Zipf pour une utilisation avancée du cache. Néanmoins, très rapidement, vous allez être confronté à des problèmes de désynchronisation de cache. Il faut alors mettre en place une solution de cache distribué. Les solutions commerciales actuelles fonctionnent plutôt bien. En revanche, il faut en même temps investir dans des outils de monitoring afin de prévenir tout phénomène de split brain. Il faut savoir que ces outils peuvent proposer des systèmes de write-behind afin de lancer des batchs asynchrones d’insertion.

NoSQL

Si malgré tous ces efforts, votre application ne tient toujours pas la charge à cause de l’accès au données, il vous reste encore une solution : NoSQL (pour Not only SQL).
Vous vous rappelez du théorème de CAP ? En général, on veut un système cohérent (contrôle d’intégrité, transaction, etc…). Et c’est ce que fait la plupart des serveurs de base de données. NoSQL se concentre sur les deux autres contraintes au détriment de la cohérence. Enfin, pour être plus précis, NoSQL indique qu’il sera cohérent à un moment donné (mais pas forcément tout le temps).
Il existe de nombreuses solutions NoSQL mais ici nous avons vu Cassandra qui serait le plus performant en écriture. En gros, c’est un cluster de données sans maître où chaque données est répliquée sur au moins 3 noeuds. Lorsque l’on interroge un noeud, il répond tout de suite avec les informations dont il dispose. Puis va chercher à se mettre à jour (d’où le à terme sera cohérent). En écriture, la gestion des conflits se fait via une date cliente. C’est donc le dernier arrivé qui l’emporte ! ;-)
Par contre, oubliez schemas, tables, colonnes, SQL et autres ! Vous manipulez un identifiant et des listes de couples clé/valeur. Celà permet de faire des choses assez sympa comme le wide-row. Mais les contre-parties sont :
  • Pas de JPA ni JDBC, il faut tout faire à la main
  • Le modèle est défini dans le métier
Pour information, il existe des drivers pour différents langages. De plus, des projets sont en cours pour faciliter le développement avec par exemple Hibernate OGM (Hibernate) ou Kundera (JPA).
Mais alors quelle solution choisir ?
  • Vous avez besoin de transaction, d’intégrité ? La base de données relationnelle est votre meilleure amie !
  • Les solutions de cache sont vraiment des pistes intéressantes.
  • Vous avez un cloud ? Aujourd’hui, aucune base de données n’est scalable via le cloud. NoSQL pourrait être salvateur.
  • Et si on mixait SGBDR et NoSQL ? :-p