Application karaf / pax web / jetty / jsp / extjs MVC
Bonjour,
Je viens de commencer un dévelopement un peut particulier de part son environnement.
J'utilise Karaf 2.2.2 qui est un conteneur osgi.
J'embarque dans celui-ci de nombreux bundles qui sont le cœur de métier de ma plateforme.
Pas grand chose à dire sur ceux-ci si ce n'est que j'utilise apache camel pour implémenter des EIP.
karaf se comporte un peu comme un j2ee si ce n'est que l'on peut déployer dans le contener plus ou moins ce que l'on veut (dans j2ee les war vont dans un conteneur de war, les ear dans un conteneur d'ear etc.).
En marge de mon besoin, j'ai donc le petit projet sur lequel je travaille aujourd'hui.
Pour tester la chose j'ai commencé par faire simple.
1) Créer avec sencha cmd une nouvelle application extjs4.2.0 => sample.
2) Suivre le tutoriel MVC.
3) Réviser le code.
Placer le proxy dans le modèle et non dans le store et mettre la même url pour read et update (java fera le nécessaire).
model/user.js
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Ext.define('AM.model.User', {
extend: 'Ext.data.Model',
fields: ['name', 'email'],
proxy: {
type: 'ajax',
api: {
read: 'data/users.json',
update: 'data/users.json'
},
reader: {
type: 'json',
root: 'users',
successProperty: 'success'
}
}
}); |
store/user.js
Code:
1 2 3 4 5
| Ext.define('AM.store.Users', {
extend: 'Ext.data.Store',
model: 'AM.model.User',
autoLoad: true
}); |
4) Builder l'application extjs avec sencha cmd.
5) Créer un projet maven "extjs". Il existe des archétypes pour le faire mais j'ai fait ça à la main.
Code:
1 2 3 4 5 6 7 8 9 10
| +---src
+---main
+---features
+---java
| +---com
| +---sencha
| +---extjs
+---webapp
+---views
+---WEB-INF |
Le fichier pom.xml (qui permet de compiler le tout)
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
| <?xml version="1.0" encoding="UTF-8"?>
<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.sencha</groupId>
<artifactId>extjs</artifactId>
<version>4.2</version>
<packaging>war</packaging>
<name>Sekaijin :: WebApp :: Karaf</name>
<description>Example of how to deploy a WebApp Over Karaf</description>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-servlet_2.5_spec</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>org.ops4j.pax.web</groupId>
<artifactId>pax-web-jsp</artifactId>
<version>1.0.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>net.minidev</groupId>
<artifactId>json-smart</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<!-- Make a skinny WAR -->
<packagingExcludes>WEB-INF/lib/*.jar</packagingExcludes>
<archive>
<manifestFile>${basedir}/target/bnd/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.3.5</version>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<supportedProjectTypes>
<supportedProjectType>war</supportedProjectType>
</supportedProjectTypes>
<manifestLocation>${basedir}/target/bnd</manifestLocation>
<instructions>
<Webapp-Context>extjs</Webapp-Context>
<Web-ContextPath>extjs</Web-ContextPath>
<Export-Package>!*</Export-Package>
<Import-Package>
javax.servlet; version="[2.4.0, 4.0.0)",
javax.servlet.http; version="[2.4.0, 4.0.0)",
javax.servlet.resources; version="[2.4.0, 4.0.0)",
*
</Import-Package>
<Bundle-ClassPath>
WEB-INF/classes,
<!-- Have to use this for PAX Web 0.7.4 to find JSPs since it uses classpath. -->
.
</Bundle-ClassPath>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>features</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>src/main/features</directory>
<filtering>true</filtering>
</resource>
</resources>
<outputDirectory>target/features</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>attach-artifacts</id>
<phase>package</phase>
<goals>
<goal>attach-artifact</goal>
</goals>
<configuration>
<artifacts>
<artifact>
<file>target/features/features.xml</file>
<type>xml</type>
<classifier>features</classifier>
</artifact>
</artifacts>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project> |
Je ne vais pas le détailler mais en gros le packaging est un war comme les autre. J'ai ajouté le plugin bundle pour générer le manifest automatiquement. Et enfin le build helper qui génère une feature.
Le manifest est une contrainte de osgi (karaf) sans lui osgi ne sais pas quoi faire du package. Utiliser maven évite d'oublier des dépendences dans le manifest.
La feature est une facilité, elle va décrire pour osgi la liste des bundles à déployer si nécessaire pour que notre war ait bien toutes les librairies dont il a besoin.
Passons au chose sérieuses.
6) Ajouter une servlet dans le dossier main/java/com/sencha/extjs§Test.java.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package com.sencha.extjs;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class Test
*/
public class Test extends HttpServlet {
private static final long serialVersionUID = -64654321713218L;
/**
* @see HttpServlet#HttpServlet()
*/
public Test() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
RequestDispatcher dispatcher = getServletContext().
getRequestDispatcher("/views/index.jsp");
dispatcher.forward(request, response);
}
} |
Cette servlet ne fait qu'appeler un jsp. Un bug dans la version de pax web embarqué par défaut dans karaf empêche d'utiliser directement la jsp.
7) Ajouter la jsp dans le dossier views.
Code:
1 2 3 4 5 6 7 8 9 10 11
| <!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title> sample <%= new java.util.Date() %></title>
<link rel="stylesheet" href="./resources/sample-all.css"/>
<script type="text/javascript" src="./all-classes.js"></script>
</head>
<body>
</body>
</html> |
Ici rien d'extraordinaire si ce n'est que c'est un copier collé du fichier index.html qu'a produit la commande sencha à l'étape 4). J'ai juste modifié le titre pour le voir changer à chaque appel. C'est par cette jsp que java passera au démarage des données à extjs si necessaire.
8) Copier le dossier ressource produit par l'étape 4) dans le dossier webapp.
9) Copier les fichiers all-classes.js et sample-all.scss produit par l'étape 4) dans le dossier webapp.
10) Ajouter un fichier web.xml dans WEB-INF.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>com.sencha.extjs.Test</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>test</servlet-name>
<url-pattern>/test.html</url-pattern>
</servlet-mapping>
</web-app> |
11) Ajouter le fichier features.xml dans le dossier features.
Code:
1 2 3 4 5 6 7 8
| <?xml version="1.0" encoding="UTF-8"?>
<features name="${project.artifactId}-${project.version}">
<feature name="${project.artifactId}" version="${project.version}">
<feature>war</feature>
<bundle>mvn:${project.groupId}/${project.artifactId}/${project.version}/war</bundle>
</feature>
</features> |
12) Builder avec mvn install.
13) Déployer dans karaf.
Code:
1 2
| addurl mvn:com.sencha/extjs/4.2/xml/features
features:install extjs |
L'application est accessible sur le l'url de la plateforme http://localhost:60080/extj/test.html.
A ce stade l'application ext se lance via java mais les url vers les données ne sont pas implémentées.
14) Ajouter une servlet User qui renvoit dans la méthode doGet les données comme indiquées dans le tuto MVC de sencha.
Et dans doPost {"sucess":true}.
15) Editer web.xml et ajouter.
Code:
1 2 3 4 5 6 7 8 9
| <servlet>
<servlet-name>user</servlet-name>
<servlet-class>com.sencha.extjs.User</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>user</servlet-name>
<url-pattern>/data/users.json</url-pattern>
</servlet-mapping> |
Re builder avec mvn et redéployer.
Le tutoriel marche complètement.
Mon but n'est pas ici de faire un tutoriel complet mais de donner les grandes lignes.
Les principales difficultés sont de maîtriser la structure de la webapp java pour rester cohérent avec maven. J'utilise eclipse. La structure d'un projet web dynamique d'éclipse ne convient pas à maven et la structure maven ne fait pas très bon ménage avec WTP (eclipse).
Bref rien à voir avec ExtJS.
Utiliser le serveur J2ee d'éclipse pour développer la partie purement sencha n'est pas une bonne idée (out of memory) le serveur interne de sencha convient bien mieux.
Cela implique de développer séparément la partie sencha et la partie java. Ce qui est pour moi une bonne chose. Dans sencha j'utilise des bouchons comme dans le tutoriel. Je n'ai qu'à les remplacer dans le mapping web.xml par des servlets.
Une question que je me posais était de savoir s'il serait compliqué de faire évoluer le projet. En fait je gère les deux parties comme deux projets. Pour mettre à jour la partie extjs du projet java il suffit de copier les éléments resources all-classes.js et sample-all.css du dossier production du projet sencha dans le dossier webapp du projet java. C'est donc plutôt simple.
Quels avantages à utiliser cette architecture ?
Outre les facilités de karaf pas grand chose par rapport à un tomcat ou autre serveur j2EE.
Mais combiné à maven ça devient intéressant.
Le bundle ainsi crée fournit un feature. Si dans la partie java on a besoin d'une librairie, maven permet de l'ajouter aux dépendances et la feature de l'ajouter à karaf.
Il devient donc rapidement envisageable de fournir des bundles qui sont des fragments d'application extjs (un bundle ne s'occupant que d'une partie bien distincte de l'application).
Ainsi on ne déploe que des fragments relativement petits qui peuvent évoluer indépendamment.
Quant à mon cas personnel, le fait que mon application web soit dans karaf me permet grâce aux fonctionnalités de osgi de dialoguer avec mes autres bundles.
A+JYT