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 : Sélectionner tout - Visualiser dans une fenêtre à part
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 : Sélectionner tout - Visualiser dans une fenêtre à part
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 : Sélectionner tout - Visualiser dans une fenêtre à part
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 xml : Sélectionner tout - Visualiser dans une fenêtre à part
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 java : Sélectionner tout - Visualiser dans une fenêtre à part
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 jsp : Sélectionner tout - Visualiser dans une fenêtre à part
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 xml : Sélectionner tout - Visualiser dans une fenêtre à part
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 xml : Sélectionner tout - Visualiser dans une fenêtre à part
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 : Sélectionner tout - Visualiser dans une fenêtre à part
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 xml : Sélectionner tout - Visualiser dans une fenêtre à part
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