mardi 26 février 2008

L'ERP opensource vu par Apache

Les projets de la fondation Apache ont ceci de particulier que leur licence permet une grande permissivité et une grande possibilité d'intégration dans d'autre projets. Il existe un projet particulièrement intérressant qui est OfBiz. Il s'agit d'une librairie destinée à la réalisation de logiciels de gestion et de site de e-commerce. Elle a permis le lancement de nombreux projets d'ERP opens-source comme Neogia ou OpenTabs.

à voir :

OFBiz

OpenTabs

Neogia

Hello BCEL

Voici un autre exemple de programme autour de BCEL. Il s'agit d'un générateur de classe "Hello World". Ce programme instancie et appel ensuite la méthode qui va bien pour afficher le message "Hello World"


import org.apache.bcel.generic.*;
import org.apache.bcel.*;
import java.io.*;

public class FabriqueDeClasseHelloWorld implements Constants {
private InstructionFactory fabriquePourInstructions;
private ConstantPoolGen listeDesConstantesDeLaNouvelleClasse;
private ClassGen classeAConstruire;

// nom de la nouvelle classe à créer
static private String classNameTarget() {
return "HelloWorld3";
}

public FabriqueDeClasseHelloWorld() {
classeAConstruire = new ClassGen(classNameTarget(), "java.lang.Object",
classNameTarget()+".java", ACC_PUBLIC | ACC_SUPER, new String[] {});
listeDesConstantesDeLaNouvelleClasse = classeAConstruire
.getConstantPool();
fabriquePourInstructions = new InstructionFactory(classeAConstruire,
listeDesConstantesDeLaNouvelleClasse);
}

public void create(OutputStream out) throws IOException {
creationDuConstructeur();
creationMethodeMain();
classeAConstruire.getJavaClass().dump(out);
}

private void creationDuConstructeur() {
InstructionList il = new InstructionList();
MethodGen method = new MethodGen(ACC_PUBLIC, Type.VOID, Type.NO_ARGS,
new String[] {}, "", classNameTarget(), il,
listeDesConstantesDeLaNouvelleClasse);

InstructionHandle ih_0 = il.append(fabriquePourInstructions.createLoad(Type.OBJECT, 0));
System.out.println(ih_0.getInstruction().getName());

InstructionHandle ins2 = il.append(fabriquePourInstructions.createInvoke(
"java.lang.Object", "", Type.VOID, Type.NO_ARGS,
Constants.INVOKESPECIAL));
System.out.println(ins2.getInstruction().getName());

InstructionHandle ih_4 = il.append(fabriquePourInstructions.createReturn(Type.VOID));
System.out.println(ih_4.getInstruction().getName());

method.setMaxStack();
method.setMaxLocals();
classeAConstruire.addMethod(method.getMethod());
il.dispose();
}

private void creationMethodeMain() {
System.out.println("\nListe des instructions de la méthode main");
InstructionList il = new InstructionList();

// on rajoute une méthode main sans paramètre
MethodGen method = new MethodGen(ACC_PUBLIC | ACC_STATIC, Type.VOID,
Type.NO_ARGS, new String[] {}, "main", classNameTarget(), il,
listeDesConstantesDeLaNouvelleClasse);

InstructionHandle ins1 = il.append(fabriquePourInstructions.createFieldAccess(
"java.lang.System", "err",
new ObjectType("java.io.PrintStream"), Constants.GETSTATIC));

System.out.println(ins1.getInstruction().getName());

InstructionHandle ins2 = il.append(new PUSH(
listeDesConstantesDeLaNouvelleClasse,
"Hello world sur la sortie erreur!"));
System.out.println(ins2.getInstruction().getName());

InstructionHandle ins3 = il.append(fabriquePourInstructions.createInvoke(
"java.io.PrintStream", "println", Type.VOID,
new Type[] { Type.STRING }, Constants.INVOKEVIRTUAL));

System.out.println(ins3.getInstruction().getName());

InstructionHandle ins4 = il.append(fabriquePourInstructions.createReturn(Type.VOID));
System.out.println(ins4.getInstruction().getName());

method.setMaxStack();
method.setMaxLocals();
// on rajoute la nouvelle méthode à la classe
classeAConstruire.addMethod(method.getMethod());
il.dispose();
}

public static void main(String[] args) throws Exception {
FabriqueDeClasseHelloWorld creator = new FabriqueDeClasseHelloWorld();

// on doit avoir un répertoire build ou se trouve les .class fabriqués
creator.create(new FileOutputStream("build/" + classNameTarget()
+ ".class"));

/*
* attention, pour que cette partie fonctionne il faut que le répertoire
* build se trouve dans le classpath
*/

// on récupère la classe que l'on vient de créer
Class cls = Class.forName(classNameTarget());

// on fait une instance de cette classe
Object obj = cls.newInstance();

// l'unique méthode est le main. On fait un appel sur ce main
obj.getClass().getMethods()[0].invoke(obj);


}
}

lundi 25 février 2008

Hello Spring

Hello World Spring

Le framework Spring implémente le design pattern IoC (inversion de contrôle). Le petit exemple qui va suivre a pour but de donner un petit exemple simple d'utilisation de cette API.

Tout d'abord on souhaite réaliser une application qui affiche "Hello World". On souhaite que cette application soit le plus paramétrable possible. En fait elle doit permettre d'afficher "Hello World" sur une sortie quelconque.

Il s'agit donc de rendre la sortie générique.

La première implémentation se ferait à l'aide de deux classes :

Pour commencer, la classe application


package net.olivier_thebault;

import net.olivier_thebault.screen.Screen;

public class HelloWorld {

public Screen scr=new Screen();

public static void main(String[] args) {
HelloWorld helloWorld=new HelloWorld();
helloWorld.scr.show("Hello world !!!");
}

}


puis une classe d'affichage :


package net.olivier_thebault.screen;

public class Screen {

public void show(String text){
System.out.println(text);
}

}

Dans cet exemple, la classe application ne connait pas la méthode d'affichage, elle sait juste qu'il existe une classe Screen. Le problème est si l'on souhaite modifier la sortie, on doit créer une nouvelle classe d'affichage, NewScreen par exemple et modifier le programme principal en conséquence. Dans un cas simple, comme celui-ci, cela n'est pas très difficile, mais imaginons simplement, que l'on est plusieurs instances de Screen, donc plusieurs appels à new Screen(), la modification du programme serait alors beaucoup plus longue et accidentogène.
En plus ce ça il faudrait recompiler l'application.

Je vais alors faire une petite modification pour mieux séparer l'affichage du programme : rajouter une interface. En fait il s'agit d'établir un contrat entre la classe principale et la classe d'affichage. Cela aura pour avantage d'externaliser la liaison entre les deux classes

On obtient alors ceci pour l'interface :

package net.olivier_thebault;

public interface IScreen {

public abstract void show(String text);

}


et ceci pour les deux autres classes :


package net.olivier_thebault;

import net.olivier_thebault.screen.Screen;

public class HelloWorld {

public IScreen scr=new Screen();

public static void main(String[] args) {
HelloWorld helloWorld=new HelloWorld();
helloWorld.scr.show("Hello world !!!");
}

}



package net.olivier_thebault.screen;

import net.olivier_thebault.IScreen;

public class Screen implements IScreen {

/* (non-Javadoc)
* @see net.olivier_thebault.screen.IScreen#show(java.lang.String)
*/
public void show(String text){
System.out.println(text);
}

}



Cela va nous permettre d'externaliser complétement la référence à la classe Screen, en introduisant une nouvelle classe. Pour commencer le champ "scr" va être initialisé par le constructeur


package net.olivier_thebault;

public class HelloWorld {

public IScreen scr;

public void hello(){
scr.show("Hello world !!!");
}

public HelloWorld(IScreen scr){
this.scr = scr;
}

}


De fait il n'existe plus aucune référence au constructeur et à la classe Screen. De plus cette classe ne sera plus la classe principale du programme, elle perd sa méthode main au profit d'une méthode Hello

La classe principale sera désormais :


package net.olivier_thebault;

import net.olivier_thebault.screen.Screen;

public class HelloMain {

public static void main(String[] args) {
HelloWorld helloWorld=new HelloWorld(new Screen());
helloWorld.hello();
}

}


D'accord, tout ce qui est fait pour le moment est de déplacer le problème vers une nouvelle classe, ce qui a tendance à complexifier la solution, qui en plus est partielle. Il est donc temps d'améliorer et simplifier tout ça. C'est à que Spring peut rendre les choses plus confortable. Pour commencer il faut créer un fichier de configuration dont le rôle est de préciser quel est l'instance à passer à HelloWorld au moment de sa construction.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean id="Hello" class="net.olivier_thebault.HelloWorld">
<constructor-arg>
<bean class="net.olivier_thebault.screen.Screen" />
</constructor-arg>
</bean>

</beans>


L'important ici se situe dans la balise bean. Id précise le nom que spring va donner à la classe. Ce nom va servir ensuite au moment de l'appel au constructeur. On précise ensuite, grace à la balise "constructor-arg" la classe implémentant l'interface IScreen. Spring va automatiquement créer l'instance qui va bien. C'est ici que l'on trouve le seul lien entre l'application et la classe d'affichage. Ce qui veut dire que l'on peut modifier l'application uniquement par paramétrage sans recompiler.


Le programme principal devient alors ceci :


package net.olivier_thebault;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;

public class HelloSpring {

public static void main(String[] args) {
// on récupère le fichier de configuration
Resource is = new FileSystemResource("config.xml");

// à partir du fichier de config on récupère une factory qui va permettre de construire
// les beans.
BeanFactory factory = new XmlBeanFactory(is);

// Le constructeur passe par spring
HelloWorld hello=(HelloWorld)factory.getBean("Hello");

hello.hello();
}

}



Voici donc un petit exemple d'Ioc via Spring. Spring permet de faire beaucoup plus de choses, de gérer les singletons, les transactions ou de créer des applications selon le modèle MVC. Il peut aussi s'interfacer avec struts ou hibernate.

dimanche 10 février 2008

le module PIC-WEB


La société olimex commercialise un petit nano serveur web expérimental basé sur le micro-contrôleur PIC 18F452 de Microchip. Ce module comprend une interface Ethernet 10Mbit construit autour d'un ENC28J60, un port RS232 pour le paramétrage, un port ICSP pour la programmation, un bouton poussoir utilisateur, une résistance ajustable pour une entrée analogique, une thermistance et un port d'extension comprenant quelques connections au microcontroleur. L'alimentation peut se faire en continu ou alternatif. La carte contient un pont de graetz, un régulateur 5V et 3.3V.

Coté logiciel une petit démo est embarqué, l'adresse IP par défaut est 192.168.0.30. Le port RS232 est paramétré à 19200 bps, 8 bits de données, pas de parité, 1 bit de stop, contrôle de flux matériel. Pour accéder au menu de paramétrage, il faut tenir enfoncé le poussoir utilisateur tout en déclenchant un reset. On peut alors changer l'adresse IP, se baser sur un DHCP, télécharger des pages via xmodem. On peut aussi télécharger ces pages via FTP. La page de démo du serveur HTTP est un simple contrôle d'une des leds de la carte et d'une broche du microcontroleur. Il permet aussi de
lire la température ambiante et de voir la valeur de réglage de la résistance ajustable.

Bien entendu on peut très bien refaire tout le logiciel embarqué, et reprogrammer la carte selon ses besoins. L'interface ICSP permet de la faire à l'aide du logiciel de microchip MPLAB. La programmation peut se faire en assembleur ou en C avec le compilateur MCC18. Toute la partie logiciel de cette carte utilise la pile TCP/IP fourni par le constructeur du PIC.

dimanche 3 février 2008

BCEL

BCEL est une librairie java qui fait partie du projet apache. Cette librairie permet de manipuler, modifier des classes java ou créer des classes jva compilés (.class). On pourrait très bien à partir de cette librairie créer un compilateur pour une jvm ou rajouter un mécanisme de persistance à la volée à des pojos qui n'en possèdent pas.

Voici un petit exemple simple qui montre comment rajouter une méthode sur une classe :


import org.apache.bcel.*;
import org.apache.bcel.generic.*;
import org.apache.bcel.classfile.*;

/**
* Cette classe est capable de s'automodifier Elle implémente l'interface
* Constants qui contient toutes les constantes nécessaire à bcel, y compris les
* instructions assembleurs de la jvm
*
* @author olivier THEBAULT
*
*/
public class ClassModifier implements Constants {
private JavaClass clazz;
private ClassGen classGen;
private ConstantPoolGen cp;

/**
* Constructeur
*
* @param clazz
* le nom de la classe à modifier
* @throws ClassNotFoundException
*/
public ClassModifier(String clazz) throws ClassNotFoundException {
this.clazz = Repository.lookupClass(clazz);
this.classGen = new ClassGen(this.clazz);
this.cp = this.classGen.getConstantPool();
}

/**
* main de test - la classe à modifier est ClassModifier
*
* @param args
*/
public static void main(String args[]) {

ClassModifier modifier;
try {
modifier = new ClassModifier("ClassModifier");
modifier.start();

} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

public void start() {
if (this.clazz != null) {
// on liste les méthodes de la classe avant d'en ajouter une
Method[] methods = classGen.getJavaClass().getMethods();
System.out.println("*** Avant d'ajouter une méthode ***");
for (int i = 0; i < methods.length; i++) {
System.out.println(methods[i]);
}

// la méthode à rajouter devra comporter une liste d'instructions
// dans le cas présent, cette liste est vide
InstructionList il = new InstructionList();

// on construit la nouvelle méthode, elle est publique et statique
MethodGen laMethodeARajouter = new MethodGen(ACC_PUBLIC
| ACC_STATIC,
Type.VOID, // elle retourne void
Type.NO_ARGS, // elle n'a pas de paramètres
new String[] {}, // Sa liste de nom de paramètres est de
// fait vide
"nouvelleMethode", // elle s'appelle "nouvelleMethode"
clazz.getClassName(),// c'est une méthode de la classe "clazz"
il, // sa liste d'instructions est il
cp);

// on ajoute la méthode "nouvelleMethode" à la classe
classGen.addMethod(laMethodeARajouter.getMethod());

// on liste les méthodes après en avoir ajouté une
methods = classGen.getJavaClass().getMethods();
System.out.println("\n*** Liste des méthodes après ajout ***\n");
for (int i = 0; i < methods.length; i++) {
System.out.println(methods[i]);
}
} else
throw new RuntimeException("Class file is null!");
}
}




Pour en savoir plus : BCEL