Thématiques principales

lundi 2 avril 2018

InvocationHandler

Nous avions parlé des annotations dans un articles precedents mais nous nous etions arreter au traitements des annotations utilisé dans le code ou dans les class mais pas celles utiliser en runtime.

Je vous propose dans cet article de réemployer l’exemple traité en gardant l’annotation Relation mais de la compléter par exemple d’une annotation Tuple qui permet d'insérer des données dans la table construite par l’annotation Relation. (Au passage on adaptera un peu cette dernière de façon à ce que la Relation ait les attribut qui vont bien…)

Commençons par l’ajout de l’attribut dans notre précédente annotation, au passage on la passe en mode RUNTIME:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Relation {
 
 String value();
}


Ensuite on va adapter le processeur. On va externaliser la partie en interface avec la BD dans une classe utilitaire permettant de faire la construction de la table avec l’attribut qui va bien:


public void createRelation(Element e) {
  Relation relation = e.getAnnotation(Relation.class);
  String rqt = this.CREATE + e.getSimpleName().toString() + "( " + relation.value() + ATTRIBUT + ");";
  System.out.println("Requete :" + rqt);
  try {
   PreparedStatement statement = DriverManager.getConnection(DB_URL, USER, PASS).prepareStatement(rqt);
   System.out.println("Mon element : " + e.getSimpleName().toString());
   statement.executeQuery();
  } catch (SQLException e1) {
   e1.printStackTrace();
  }
 }

Voila, normalement on aura un attribut name dans notre table lors du build.

Maintenant on veut qu'à l'exécution, pour chaque objet de type Helloworld on insere dans la table le nom passé en parametre du setter. .

Pour cela on va creer l’annotation AddValue (ok c’est pas original) de façon a pouvoir la placer sur cette methode:


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AddValue {

}


Ainsi on l’utilisera ainsi:


@Relation(value = "name")
public class HelloWorld {
 private String name;
 
 @AddValue
 public void setName(String name)
 {
  this.name=name;
 }
}


Mais me direz vous, cela ne permet pas vraiment de mapper un quelconque processus d’insertion en base de la donnée! Effectivement car il manque la partie processeur de cette annotation.

Le problème ici c’est que l’annotation doit être traité pendant l'exécution. Alors comment faire?

Et bien tout simplement en utilisant deux patterns, la factory et le pattern proxy. Le premier va nous permettre de masquer l’utilisation du second qui sera couplé à l’invocation handler! Le voila celui la!

Alors avant cela voici ce que nous espérons pouvoir faire:


IHelloWorld o=MyAnnotFactory.mapObject(IHelloWorld.class,HelloWorld.class);
o.setName("Thomas");


Et ceci doit nous permettre de l'insertion de Thomas dans la base lors de l’affectation. Pour cela, la factory va utiliser une interface et le prototype de la classe pour construire une instance qui sera en réalité un proxy.


 public static  T mapObject(Class i,Class t) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException
 {
  T proxy=(T) Proxy.newProxyInstance(MyAnnotFactory.class.getClassLoader(),new Class[]{i}, new MyInvocationHandler(t));
  return proxy;
 }


Ainsi on utilise l’api Java Proxy permettant de construire ce proxy a partir de d’une interface identifiant au proxy quels sont les méthodes a wrapper et pour lesquelles, le proxy devra appeler l’InvocationHandler à la rescousse. Voyons comment, ce n’est pas tres compliqué:


public class MyInvocationHandler implements InvocationHandler {

 private Object object;

 public MyInvocationHandler(Class t) {
  try {
   this.object = t.newInstance();
  } catch (InstantiationException | IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }

 @Override
 public Object invoke(Object arg0, Method method, Object[] arg2) throws Throwable {
  System.out.println("Methode :" + method.getName());
  Method m=object.getClass().getMethod(method.getName(),method.getParameterTypes());
  for (Annotation annot : m.getAnnotations()) {
   System.out.println(annot); 
   if (annot.annotationType().equals(AddValue.class)) {
    System.out.println("je pousse en base");
    AnnotDBUtils.getInstance().pushData(object,arg2);
    return object;
   }
  }
  return method.invoke(object, arg2);
 }
}


Alors tout d’abord, le proxy doit continuer à faire croire que l’on manipule l’objet en question. (la factory etant la pour le tour de passe passe…) Du coup, a peine appelé, l’invocation handler va se charger de se construire un objet du type masqué par le proxy afin de l’utiliser lors de l'appelle effectif des méthodes sur le proxy.



Ceci va permettre de tromper l’utilisateur mais en plus cela va nous permettre de compléter ce comportement. Ainsi le contenu de la méthode invoke décrit la validation de la présence de l’annotation AddValue qui lorsqu’elle est présente va appeler une méthode push Data de notre utilitaire de BD que voici:


 public void pushData(Object object, Object[] arg2) {
  Relation relation = object.getClass().getAnnotation(Relation.class);
  String rqt = this.INSERT + "\"" + object.getClass().getSimpleName().toLowerCase() + "\" (" + relation.value()
    + ")" + VALUES + "('" + arg2[0] + "');";
  System.out.println("Requete :" + rqt);
  try {
   PreparedStatement statement = DriverManager.getConnection(DB_URL, USER, PASS).prepareStatement(rqt);
   statement.executeUpdate();
  } catch (SQLException e) {
   e.printStackTrace();
  }
 }


Voilà alors bien sur a chaque appele de la méthode setName, l’Invocation Handler va tenter de pousser la nouvelle donnée en BD sans chercher à savoir si c’est cohérent logique ou autre. Pour aller plus loin, il faudrait donner des moyens de vérification si la donnée n’est pas déjà en base ou ajouter un attribut servant de clef primaire…. mais la ca complique beaucoup les chose, et bon on va pas reimplementer JPA… même si l’idée est la même!

Du coup on va s'arrêter là mais j’imagine que vous avez compris l'intérêt de ce pattern surtout lorsqu’il est couplé aux annotations!

Références:

[1] http://alain-defrance.developpez.com/articles/Java/J2SE/implementation-dynamique-java-proxy-invocationhandler/
[2] https://un-est-tout-et-tout-est-un.blogspot.fr/2017/11/design-pattern-proxy.html
[3] http://un-est-tout-et-tout-est-un.blogspot.com/2017/11/design-pattern-factory.html

mercredi 28 mars 2018

Architectures types

Lorsque nous nous sommes intéressé au pattern DAO [1], nous avons pu voir que les systèmes logiciels se conçoivent en définissant une architecture répondant au mieux aux besoins clients en usant de différentes stratégies en terme de solutions techniques [2] (les patterns de conceptions, les frameworks, etc…) et de méthodes [3] (IDM, Agilité, Scrum, etc...).

Ainsi, au fils des articles nous avons croisé un certain nombre de patterns ou de framework qui clairement nous simplifie la vie pour produire notre application. Cependant ce genre de concepts se focalisent exclusivement sur le logiciel lui même sans fournir de réponses à des besoins environnementaux qui peuvent être nombreux et contraignants

Ainsi par exemple, que faire lorsque le logiciel doit être capable de gérer un taux de connexion élevé? Comment garantir la monté en charge? Comment s’assurer de l'intégrité des données ? de leur disponibilités ? de leur sauvegarde? Comment garantir qu’il n’y aura pas d'interruption de service? A toutes ces questions, le logiciel seul ne peut y répondre en plus du besoin pour lequel il est produit. En effet, si techniquement on peut imaginer répondre à ces problématiques, cela nécessiterait des développement spécifiques très lourd en plus du métier pour lequel le logiciel doit être fait et comme nous l’avons vu précédemment, toutes les approches purement logiciel sont beaucoup trop auto-centré.

A cette problématique existe heureusement un solution et celle-ci relève en fait du bon sens. Ainsi, si un logiciel doit être conçu et architecturé, son environnement doit l'être aussi! Et les choix d’architectures (interne et externe au logiciel) doivent permettre à ces deux systèmes de cohabiter.

Nous allons donc prendre un peu de recul sur la conception logiciel elle même et nous placer au dessus des patterns de conception que nous avons vu jusque ici pour regarder le logiciel de façon plus global. L’objectif est toujours de considérer le logiciel avec ses interfaces mais aussi de regarder au delà de celles ci pour penser l’architecture dans son ensemble.

Reprenons pour commencer l’exemple très simple de notre article sur le DAO [1]. Nous avions vu l’une des architectures les plus typiques, l’architecture en 3 couches permettant de dissocier les préoccupations métiers des préoccupations de représentations (la couche presentation) des préoccupations de stockage (la couches de données).

Il est évident que cette approche a de nombreux avantages, dans la conception d’une part car les spécificités des besoins des différentes couches seront isolés et confiés aux experts des différents domaines associés à ses couches. Dans la maintenance également car l'indépendance permet de substituer n’importe quelle couche par d’autres implémentations équivalentes ou même d’autres solutions techniques, tant que les interfaces entre couches sont respectées. Enfin ce genre d’approche faisant consensus parmis les développeurs, elle permet la mise en place de standard tant au niveau méthodologique qu’au niveau technique et aujourd’hui, parler par exemple de l’utilisation d’une architecture JEE en 3 couches fait tout de suite référence aux technologies JSP, JSF, Servlet pour la couche présentation, JPA, Hibernate pour les couches données tout en exploitant des EJB pour les couches métiers. Bien sur le modèle en 3 couches a ses limites et souvent il est necessaire de considerer quelques couches supplémentaires en ajoutant une couche de sécurité entre la présentation et le métier et une couche de cache entre le métier et la couche de données. Nous en obtenons alors le modèle en 5 couches [5].


Bien sur on peut en déduire des modèles à n couches mais cette approche a elle même des limites car en augmentant le nombre de couches, on augmente également le temps de traversé des flux de contrôle et de données, occasionnant une chute des performances.

Pour répondre à cette problématique, heureusement il existe également des solutions. Le problème de l’architecture en couche est d'être vertical. Ainsi, une solution est de fournir des fonctionnalités transverses déployées de manière horizontale.

De façon à concevoir des architectures plus horizontale, une approche dite orientée service est possible ou SOA [7]. Celle ci est apparue peu avant 2000 et a largement bénéficié de l'émergence des webservices. Le principe de SOA est simple, il se base sur le principe du producteur consommateur avec comme produit le service.

Pour orchestrer ces communications [8], un annuaire (UDDI) permet la définition d’un référentiel des services accessibles sur un bus de communication servant de middleware (souvent un ESB mais pouvant reposer plus simplement sur le Web si la sécurisation est complètement pris en compte). Enfin tous ces éléments vont reposer sur le standard xml pour faciliter l'interopérabilité de la communication et l'échange des données en restant conforme aux contrats définis pour les services en exploitant des support de communication comme SOAP ou JMS.

L'intérêt de ce genre d’architecture est clairement la mise en place d’un découplage entre les fonctions fourni par les services permettant à des équipes différentes de les développer indépendamment, sur des cibles potentiellement hétérogènes (OS, frameworks, language). de plus cela permet de faire vivre dans le même environnement des applications requièrant les même services ou partiellement. De même faire évoluer ce type de système se résume alors à l’identification des services à exploiter et des nouveaux à déployer.

La réside la difficulté de cette approche. Il faut être capable de dimensionner correctement les services de façon à ce qu’ils soient ni trop simple au risque d'être trop exploité avec des risques notable de perte de performances (à noter qu’un service est censé être unique donc c’est à lui de gérer l’exploitation de la demande et la monté en charge) soit trop complexe et donc trop peu réutilisable. Les architectures orienté service sont donc souvent confronté aux problèmes de performances qui sont directement relié aux capacités réseaux et aux capacités en temps de réponse des plus fragiles des services du système ( qui peuvent en ralentir ou d'encombrer drastiquement le fonctionnement) A noter que ce n’est pas le seul point noir car, si ls SOA favorise la modularité et le reuse des services, cela ne favorise pas son evoluabilité. Toutes transformations dans la chaîne des processus EBP (Enterprise Business Process) utilisant les services entraînera de forte perturbation sur l’ESB.

Pour palier à ces défauts les microservices proposent de descendre encore en granularité dans la décomposition fonctionnelle. Ainsi un microservice va se réduire à ne répondre qu'à une et une unique fonction tout en simplifiant les protocoles de communication par l’utilisation de REST par exemple et en supprimant le principe de bus. L’idée principale est de produire une fournir un plus grand nombre de services (en comparaison à SOA) mais plus simple, élémentaire et donc plus indépendant. Cela va alors faciliter leur maintenance et leur interchangeabilité.
Les microservices ont, au delà des aspects techniques, des avantages même au niveaux processus car grâce à leur indépendance, il est possible de morceler plus le système et donc de le rendre plus facilement testable. Cela conduit même au rapprochement du développeur dans cette phase de test qui aura la possibilité d’isoler complètement le service qu’il doit produire (on le voit arriver la le docker? ). Finalement, grâce à cela, l’utilisation des microservices va déplacer nos problématiques précédentes de performance et de maintenance en une problématique d’interconnexion et de logique de définition de processus métier. Attention alors à ne pas tomber dans des architectures plat de spaghetti…[6]

Jusque là nous nous sommes projetés dans des architectures sans parler de la structure réseau sous-jacente. Bien sur aujourd’hui une application ne se pense plus de façon stand alone sur un serveur, elles se pensent embarquées dans un ou plusieurs serveurs d’applications répartis sur plusieurs serveurs physiques et les approches de types SOA ou Microservices se prêtent parfaitement à cela mais si l’on ne considère pas forcément ce type de solutions, pourquoi malgré tout choisir une architecture distribuée et/ou décentralisé?

En réalité la réponse est simple, et c’est toujours la même, une architecture bien pensé doit permettre d'éviter de construire des monolithes immaintenables (remarque, si ca nous fait évoluer...) et la décomposition est souvent la première des bonnes idées à avoir.

Nous avons vu que ces principes font parti intégrante des architectures précédentes. En fait si les architectures distribuées et/ou décentralisées sont intéressantes c’est parce que non seulement elle poussent à décomposer le problème et favoriser ainsi la réutilisabilité, la maintenabilité (le problème étant localement plus simple du coup) mais elles permettent aussi d'intégrer des solutions techniques répondant à des problèmes plus spécifiques et techniques exigés par le client comme des exigences de disponibilités, de montées en charges, de vitesse de traitement, etc...

source: Architecture fédérée

Par contre, il ne faut pas croire que parce que l’on va considérer une architecture distribuée et ou décentralisé, les problèmes sont terminées car cela peut aussi être une fausse bonne idées dans certains cas. En effet, souvent ce genre d'architecture nécessite d'incorporer au mieux des outils spécifiques permettant la gestion du problème considéré mais selon les cas, il faudra penser à des mécanismes spécifiquement développées par l'équipe de développement. Dans ce genre de cas, on s’engage souvent sur un chemin complexe car ce genre d'architecture ne sont pas simple à gérer soit même. Par exemple, si nous considérons le cas d’une architecture orienté données qu’est l’architecture fédérée, elle a pour objet de permettre la mise en collaboration de différentes bases de données pour donner l’illusion qu’elle n’en forme qu’une tout en leur permettant de rester indépendantes. Si l’idée est pertinente, dans la pratique (dans le cas de fédérations faiblement couplé), elle nécessite de constituer des vue spécifiques dans chacunes des bases, des schémas supplémentaires (privé : base initiale , export : données présenté aux autres bases, import : données reconstitué des autres base) et des mécanismes de reconstruction. Ce genre d’approche est très complexe à mener et doit être refait pour à chaque intégration d’une nouvelle base dans la fédération. Il sera probablement plus simple de réaliser une migration des données vers des bases repensées plus globalement.

Alors bien sur le cas des architectures fédérées n’est pas à généraliser et elle peuvent aussi trouver leur applications dans certains cas bien étudié. Il ne faut pas oblitérer que ce genre d’architecture permettent aussi de gérer des problématiques de redondances ou de répartition de charge pour obtenir des services en haute disponibilité.

Pour conclure cet article déjà assez long, on statuera sur le fait que toutes les architectures décrites jusqu'ici sont plutôt classiques alors aujourd’hui, de nouvelles architectures font leur apparition pour intégrer de nouveaux types de besoins comme l'intégration de service dans le cloud, le traitement de données massives, le besoin de procéder à des prévisions ou des profils de ventes grâce à du Big Data ou du Machine learning. Nous ne rentrerons pas dans le détails de celles ci maintenant mais nous y reviendrons.

Références:

[1] https://un-est-tout-et-tout-est-un.blogspot.fr/2018/03/design-pattern-dao.html
[2] https://un-est-tout-et-tout-est-un.blogspot.fr/2017/10/la-conception-logicielle.html
[3] https://un-est-tout-et-tout-est-un.blogspot.fr/2017/12/la-modelisation.html
[4] https://un-est-tout-et-tout-est-un.blogspot.fr/p/java.html
[5] https://jobprod.com/quelques-principes-darchitecture-dapplis-web-javajee/
[6] https://dzone.com/articles/microservices-vs-soa-is-there-any-difference-at-al
[7] https://www.piloter.org/techno/support/SOA.htm
[8] https://openclassrooms.com/courses/implementez-une-architecture-orientee-services-soa-en-java/mprendrecequecestunesoa

samedi 24 mars 2018

Maven : assembly, targz et jdeb

Aujourd’hui nous allons nous intéresser à la production du livrable issue de la production logicielle. Le sujet ne va pas etre forcement tres long a traiter tant maven permet de chose si l’on utilise les bons plugins.

La production du livrable impose avant tout la définition de celui ci. Souvent, il va être dépendant de la plateforme et du langage. Dans notre cas, nous allons tâcher de produire nos composants dans le contexte d’utilisation du langage Java et nous nous projeterons dans une plateforme linux, la debian et la plateforme windows.

Bien sûr on aurait pu passer par l’utilisation de profile maven dans lequel nous aurions spécifier selon l’OS différents plugins afin de répondre à cette problématique de production. Par exemple en définissant les profiles suivants:

<profiles>
 <profile>
  <activation>
   <os>
    <family>windows</family>
   </os>
  </activation>
  <build>...</build>
  ...
 </profile>
 <profile>
  <activation>
   <os>
    <family>linux</family>
   </os>
  </activation>
  <build>...</build>
  ...
 </profile>
</profiles>


Nous n'utilisons pas cette approche car d’une part non nécessaire puisque Java est multiplateforme et d’autre part elle complexifie lourdement la production (l’utilisation de profile n’etant une bonne chose que si on en peut vraiment pas faire autrement)

Au lieu de cela, nous allons plutôt tricher un peu et prendre le parti pris que nous souhaitons livrer simplement un targz pour windows et un deb sous linux (comme cela, on pourra produire le même jar et livrer d’un côté un script bash pour lancer notre appli et de l’autre un bat.) Pour répondre à ce problème nous allons utiliser le plugin maven-assembly-plugin.

Ce plugin est un plugin indispensable pour la plupart des builds maven car au delà de l’utilisation ce que nous allons en faire, il permet de faire surtout toute sorte de compositions, de construire tout type de paquet de livraison mais aussi de pouvoir construire des jar au contenu spécifique (nous y reviendrons mais pas dans cet article) .

Nous avons donc deux types de packaging à réaliser, un pour le targz et un autre pour le deb. On va configurer le plugin pour qu’il fasse les deux en même temps en définissant deux fichiers d’assembly:

<plugin>
 <groupid>org.apache.maven.plugins</groupid>
 <artifactid>maven-assembly-plugin</artifactid>
 <version>2.5.3</version>
      <configuration>
  <finalname>${project.artifactId}-${project.version}</finalname>
  <ignoredirformatextensions>true</ignoredirformatextensions>
  <outputdirectory>${project.build.directory}</outputdirectory>
       </configuration>

 <executions>
  <!-- Packaging debian -->
  <execution>
   <id>assembly-debian</id>
   <phase>prepare-package</phase>
   <goals>
    <goal>single</goal>
   </goals>
   <configuration>
    <appendassemblyid>false</appendassemblyid>
    <descriptors>
     <descriptor>src/main/assembly/assembly-debian.xml</descriptor>
    </descriptors>
   </configuration>
  </execution>
  <execution>
   <id>assembly-targz</id>
   <phase>package</phase>
   <goals>
    <goal>single</goal>
   </goals>
   <configuration>
    <appendassemblyid>false</appendassemblyid>
    <descriptors>
     <descriptor>src/main/assembly/assembly-targz.xml</descriptor>
    </descriptors>
   </configuration>
  </execution>
 </executions>
</plugin>


Quelques explications sur l’utilisation du plugin dans le pom. On précise le répertoire de sortie et le nom général de la composition. On spécifie ensuite par type de composition un élément de configuration en utilisant appendAssemblyId. Ceci permet d’ajouter le classifier “assembly” au nom de la composition, mais ici on va s’en passer. On met false. On précise également pendant quelle phase et quel goal les assembly devront être construit. Enfin on donne au plugin le fichier d’assembly qui précise le contenu de la composition.

Le fichier assembly est la dernière partie permettant de construire la composition. Il s’agit d’un fichier xml définissant plusieur choses: le format de sortie, les dépendances à joindre et ou les déposer et enfin les répertoires annexes que la composition doit contenir. Regardons celui du targz:

<assembly>
 <id>bundle</id>
 <formats>
  <format>tar.gz</format>
 </formats>
 <includebasedirectory>false</includebasedirectory>

 <dependencysets>
<!--  Inclusion interfaces classifier et librairies-->
  <dependencyset>
   <unpack>false</unpack>
   <scope>runtime</scope>
   <useprojectartifact>false</useprojectartifact>
   <useprojectattachments>true</useprojectattachments>
   <outputdirectory>/opt/monappli</outputdirectory>
   <includes>
    <include>*:${project.artifactId}:*:assembly:*</include>
   </includes>
  </dependencyset>
 </dependencysets>

  <filesets>
      <fileset>
          <directory>src/main/debian/etc/init.d</directory>
          <outputdirectory>/etc/init.d</outputdirectory>
          <usedefaultexcludes>true</usedefaultexcludes>
          <filtered>true</filtered>
          <includes>
              <include>monappli</include>
          </includes>
      </fileset>
  </filesets>
</assembly>


Inutile de commenter le format, il parle de lui-même. Les dependencySet se construisent pour incorporer à la composition les éléments des dépendances qu’il faudra ajouter et sous quel forme. Par exemple on peut spécifier si elles seront dézipper dans la composition (cela est pratique par exemple lorsque l’on veut fusionner des artifacts ensemble pour faire un jar), le répertoire ou ces éléments seront déposé et enfin un ensemble d'artifact déclarer sous la forme d’une regex.

Les fileSet précisent de la même manière que pour les artifacts l’ensemble des fichiers et répertoires à construire dans notre composition. Ces éléments sont généralement issus des ressources du projets et il est possible de réaliser du remplacement clef/valeur dedans (via le resources filtering). A l’issu de l’application de l’assembly, on aura alors tous ces éléments dans un tar.gz… cool!

Bon bien du coup on fait pareil pour le paquet debian non? et bien pas tout à fait. sur le principe, on va procéder de la même manière en utilisant un fichier d’assembly rigoureusement identique sauf sur le format que nous définirons a “dir”. Ce format ne fait rien a part préparer les éléments de la composition. Pourquoi prendre ce format? Tout simplement parce que le plugin assembly n’est pas capable de construire quelque chose d’aussi complexe qu’un paquet debian. On va donc s’appuyer d’un autre plugin pour faire cela: jdeb. Avant cela je vous renvoie à [3] pour compléter vos ressources avec les fichiers de contrôles qui vont bien et les intégrer à l’assembly.

Ceci étant fait, penchons nous sur le plugin jdeb. Celui ci se configure assez classiquement en précisant sa phase de build et son goal. A cela, on va preciser le nom du paquet debian, le repertoire des fichiers de controles, et enfin un ensemble de dataset precisant tous les repertoire, fichiers et autres resources qui va falloir ajouter au paquet. Attention, a ce nieau, il faut avoir une vision clair des droits qui seront appliqué lors de l’installation, puisque l’on va les preciser ici. Voyons cela:

<plugin>
 <artifactid>jdeb</artifactid>
 <groupid>org.vafer</groupid>
 <version>1.5</version>
 <executions>
  <execution>
   <phase>package</phase>
   <goals>
    <goal>jdeb</goal>
   </goals>
   <configuration>
    <deb>${project.build.directory}/${project.artifactId}-${build}_${versiontimestamp}_all.deb</deb>
    <verbose>true</verbose>
    <controldir>${project.build.directory}/${project.artifactId}-${project.version}/DEBIAN</controldir>
    <dataset>
     <data>
      <src>${project.build.directory}/${project.artifactId}-${project.version}/opt</src>
      <type>directory</type>
      <mapper>
       <type>perm</type>
       <prefix>/opt</prefix>
      </mapper>
     </data>
    </dataset>
   </configuration>
  </execution>
 </executions>
</plugin>


Voilà, nous venons de passer en revu comment construire les livrables de nos projets. Bien sur faire du java permet déjà de packager les sources mais pour être propre, l'idéal est de fournir un composant standard comme un targz ou mieux un deb contenant tous les éléments permettant de faire vivre l’application (fichier de conf, de scripts, etc…)

Références: 

[1] https://maven.apache.org/plugins/maven-assembly-plugin/single-mojo.html
[2] https://github.com/tcurdt/jdeb/blob/master/src/examples/maven/pom.xml
[3] https://un-est-tout-et-tout-est-un.blogspot.fr/2017/12/construire-un-paquet-debian.html

mercredi 21 mars 2018

JEE : Petite Intro

Nous avons traité dans quelques articles précédents de l'évolution de Java en partant de la version 1.4 jusqu'à presque aujourd’hui et la version 9 [1]. Il faut savoir qu’au fils de ces années, cette évolution a été suivi par sa version Entreprise. Celle ci nommé sous l’acronyme de J2EE (Java 2 Enterprise Edition) quand java n’en était encore qu'à la version 1.4, elle porte maintenant l’acronyme simplifié JEE (Java Enterprise Edition).

Ainsi avant d’entrer dans les détails techniques propres à cette technologie, je vous propose de faire un petit tour d’horizon des fonctionnalités offertes par cette version entreprise de Java [2].

Java pour l’entreprise est apparue en 1998 et a rapidement intégré les premières API importante du noyau JEE que l’on connaît encore bien aujourd’hui avec les :Servlets, JSP, EJB, JTA, JNDI, JavaMail et JMS (on notera aussi des extension JDBC car JPA n’existait pas encore et JAF).

Rapidement se sont greffés des API très connus comme la JSTL, JAXP (parseur xml) et moins connu comme JCA (Java Connector Architecture et non Java Cryptography Architecture de la version standard de Java) et JAAS (Java Authentication and Authorization Services qui en fait a ete ajoute au java standard mais qui va beaucoup plus etre utile sur les application web et donc JEE).

C’est avec la version 1.4 que J2EE a commencé à se segmenter autour de trois axes. Les techno Web (WebServices, XML, Servlets, JSP, JSTL et les nouvelles JSF), les technos d’entreprises ( avec les fameux et redoutés EJB 2) et enfin les technos de management et de sécurité (JMX et JACC mais oublions le…. ^^)

L’arrivée de Java 5 a été un bouleversement pour l'écosystème java. Java EE 5 a fait de même. La partie Web s’est enrichie de StAX pour le streaming Xml et de SAOP tandis que la partie Entreprise a enfin vu naître les EJB 3 et JPA (issu des techno Hibernate).

JEE 6 a introduit la notion de WebProfile permettant une simplification de la certification JEE pour les conteneurs applicatif leger. On a ainsi pu avoir des conteneurs d’applications se focalisant essentiellement sur le seul respect du web profile. Le WebProfile ne contient par définition que les parties cœurs de JEE, c’est à dire la partie Web (JSP,JSTL, Servlet, etc) en excluant la partie Web service et la Partie Entreprise, c’est à dire les EJB (en version simplifié “Lite”) JPA, et JTA. La version 6 de JEE a aussi amener de nouvelles API: un enrichissement de la partie Web Service avec JAX-RS et JAXM des ajouts dans la partie entreprise comme CDI (issu des technos Spring), les interceptons (équivalent aux filtres de la partie servlet) [3,4]

En 2013 est ensuite venu JEE 7 avec l’ambition de renouveler les technologies permettant la communication entre le front et le back. La partie Web a donc intégré un parseur JSON, l’API WebSocket (tout ça pour HTML5) et l'amélioration des servlets en proposant un mécanisme asynchrone et donc des outils pour gérer la concurrence au sein de la partie Entreprise. A cela, cette composante de JEE s’est également doté d’un mécanisme de Batch permettant l'exécution de traitement spécifique, parallélisable, etc… Donc pas de grosse révolution mais une volonté clairement affichée de suivre le mouvement, a un moment ou le monde du dev à basculer dans le BigData, le cloud, les archi distribuées, (l’IA), etc… [5]

Enfin Java EE 8 ! Sortie en septembre dernier [6], elle repose sur le socle de Java 9. Dans la continuité de JEE 7 ou l’on sentait bien une volonté de tendre vers les technologies Cloud, BD et distribué, celle ci propose des évolutions technique de l’existant et quelque nouveauté comme la prise en charge de la nouvelle norme HTTP/2 ou l'ajout de l'API JSON-B. Certains diront que son évolution a manqué le coche cette année mais JEE 9 étant prévu cette année, JEE 8 doit plutôt être vu comme un temps de pause et une bonne prise d’appuis (avec le passage à java 9 et ses modules) avant un nouveau rebond (espérons le!)

Voila, un petit tour d’horizon historique sur JEE qui n’engage que moi qui attend de voir ce que nous donnera les futur évolutions. En attendant celle-ci, je vous proposerai donc de poursuivre cet article par une liste d’articles traitant du contenu technique de JEE par la pratique. Cet article en fait donc une petite introduction que, j'espère, vous aurez appréciée.

Référence 

[1] https://un-est-tout-et-tout-est-un.blogspot.fr/p/java.html
[2] https://en.wikipedia.org/wiki/Java_EE_version_history
[3] http://alexander.holbreich.org/javaee5-vs-javaee6/
[4] https://blog.xebia.fr/2012/02/01/java-ee-6-une-plateforme-simple-et-legere-spring-na-qua-bien-se-tenir/
[5] http://blog.ippon.fr/2013/06/18/java-ee-7-plus-quun-nouveau-numero-de-version/
[6] http://www.oracle.com/technetwork/java/javaee/tech/index.html