IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

OGSi Java Discussion :

Osgi Web App Modulaire


Sujet :

OGSi Java

  1. #1
    Expert éminent
    Avatar de sekaijin
    Homme Profil pro
    Urbaniste
    Inscrit en
    Juillet 2004
    Messages
    4 205
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Urbaniste
    Secteur : Santé

    Informations forums :
    Inscription : Juillet 2004
    Messages : 4 205
    Points : 9 127
    Points
    9 127
    Par défaut Osgi Web App Modulaire
    Bonjour.

    Je cherchais depuis quelque temps comment faire une web app modulaire avec OSGI.
    le but étant de déployer dynamiquement des modules qui se retrouve automatiquement référencé dans l'application sans intervention.

    Je suis tombé sur le blog de AYOMA WIJETHUNGA
    Son exemple fut donc un point de départ.
    Je n'ai pas cherché à faire quelque chose de beau, mais à mettre en place une architecture réutilisable.
    Tout comme Ayoma je me suis basé sur Pax Web Whiteboard.
    Mais j'ai cherché à n'écrire que le strict minimum en java. (on peut faire mieux)

    Mon idée était donc d'utiliser osgi blueprint pour déployer des bundles faisant appel à Pax Whiteboard pour déclarer des servlets.
    Ayoma l'a fait en full Java.

    Un module minimal se résume donc à des classes servlets et un fichier blueprint.

    Restait un point comment faire pour que la webapp ajoute dans son contexte les éléments permettant aux utilisateurs d'accéder au dit module.
    Ayoma propose une solution via une API pour alimenter un menu.
    Lorsqu’un module démarre, l'application le détecte et récupère les informations du menu de ce module pour les ajouter au menu principal.

    J'ai donc cherché à faire de même en utilisant blueprint et en cherchant à généraliser le principe.
    Mettre en place une mécanique qui permet à une webapp de définir un ou plusieurs API et à des modules de les implementer.
    Pour cela le pattern whiteboard était tout désigné.

    Le tout se résume donc à un petit ensemble de bundles.
    modules-whiteboard le tableau blanc abstrait permettant à une webapp de partager des API
    main-api définie une interface IModule et une classe MenuItem partagées entre la webapp main et les modules
    Main un bundle qui démarre la webapp il s'agit d'un simple bundle contenant un servlet, mais aussi le lanceur du tableau blanc.
    User un bundle avec 2 servlets
    Message un bundle avec 4 servlets.

    Pour faciliter le dev, j'utilise maven et maven-bundle-plugin
    s'ajoutent donc deux projets parents
    Code text : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    parent
       module-whiteboard
       main-api
       modules
          main
          user
          message
    pour tester la solution j'ai déployé servicemix 6.1.2 (Karaf 3.0.7 est suffisant)

    le projet parent
    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
    <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/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<groupId>fr.sekaijin.osgi.web</groupId>
    	<artifactId>parent</artifactId>
    	<version>0.0.1</version>
    	<packaging>pom</packaging>
    	<name>Sekaijin :: Web :: Parent</name>
     
    	<modules>
    		<module>main-api</module>
    		<module>modules</module>
    		<module>module-whiteboard</module>
    	</modules>
    	<properties>
    		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    		<maven.compiler.source>1.7</maven.compiler.source>
    		<maven.compiler.target>1.7</maven.compiler.target>
    		<slf4j.version>1.6.1</slf4j.version>
    		<web.context>osgi</web.context>
    	</properties>
    	<dependencies>
    		<dependency>
    			<groupId>org.slf4j</groupId>
    			<artifactId>slf4j-api</artifactId>
    			<version>${slf4j.version}</version>
    		</dependency>
    		<dependency>
    			<groupId>org.slf4j</groupId>
    			<artifactId>slf4j-log4j12</artifactId>
    			<version>${slf4j.version}</version>
    		</dependency>
    	</dependencies>
    </project>
    Ici rien de bien compliqué. Seul point particulier la présence de la propriété web.context qui est utilisée pour définir le webcontext dans le bundle main, mais qui sera utilisé dans les autres.
    Pour rester dans les définitions purement maven le projet modules destinés à accueillir tous les modules.
    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
    <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/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<parent>
    		<groupId>fr.sekaijin.osgi.web</groupId>
    		<artifactId>parent</artifactId>
    		<version>0.0.1</version>
    	</parent>
    	<artifactId>modules</artifactId>
    	<packaging>pom</packaging>
    	<name>Sekaijin :: Web :: Modules</name>
    	<modules>
    		<module>main</module>
    		<module>user</module>
    		<module>message</module>
    	</modules>
     
    	<dependencies>
    		<dependency>
    			<groupId>fr.sekaijin.osgi.web</groupId>
    			<artifactId>main-api</artifactId>
    			<version>0.0.1</version>
    			<scope>provided</scope>
    		</dependency>
    		<dependency>
    			<groupId>javax.servlet</groupId>
    			<artifactId>servlet-api</artifactId>
    			<version>2.5</version>
    			<scope>provided</scope>
    		</dependency>
    	</dependencies>
    </project>
    Là encore rien de particulier.
    Le projet main-api celui-ci n'est là que pour partager une classe et une interface.
    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
    <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/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<parent>
    		<groupId>fr.sekaijin.osgi.web</groupId>
    		<artifactId>parent</artifactId>
    		<version>0.0.1</version>
    	</parent>
    	<artifactId>main-api</artifactId>
    	<packaging>bundle</packaging>
    	<name>Sekaijin :: Web :: Main   API</name>
     
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.apache.felix</groupId>
    				<artifactId>maven-bundle-plugin</artifactId>
    				<version>3.2.0</version>
    				<extensions>true</extensions>
    				<configuration>
    					<supportedProjectTypes>
    						<supportedProjectType>bundle</supportedProjectType>
    					</supportedProjectTypes>
    					<instructions>
    						<Bundle-SymbolicName>${web.context}-${project.artifactId}</Bundle-SymbolicName>
    						<Bundle-Name>${project.name}</Bundle-Name>
    					</instructions>
    				</configuration>
    			</plugin>
    		</plugins>
    	</build>
    </project>
    Code java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package fr.sekaijin.osgi.web.main.api;
     
    import java.util.List;
     
    public interface IModule {
     
    	public String getModuleName();
     
    	public List<MenuItem> getMenuItems();
    }
    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
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    package fr.sekaijin.osgi.web.main.api;
     
    public class MenuItem {
    	private String key;
    	private String path;
    	private String moduleName;
    	private String icon;
     
    	public MenuItem(String moduleName, String icon, String key, String path) {
    		super();
    		this.moduleName= moduleName;
    		this.icon = icon;
    		this.key = key;
    		this.path = path;
    	}
     
    	public String getModuleName() {
    		return moduleName;
    	}
     
    	public void setModuleName(String moduleName) {
    		this.moduleName = moduleName;
    	}
     
     
    	public String getIcon() {
    		return icon;
    	}
     
    	public void setIcon(String icon) {
    		this.icon = icon;
    	}
     
    	public MenuItem(String key) {
    		super();
    		this.key = key;
    	}
     
    	public String getKey() {
    		return key;
    	}
     
    	public void setKey(String key) {
    		this.key = key;
    	}
     
    	public String getPath() {
    		return path;
    	}
     
    	public void setPath(String path) {
    		this.path = path;
    	}
    }
    Les modules vont déclarer des menus en implémentant l'interface et la webapp va pouvoir les utiliser.

    La suite dans le prochain post
    A+JYT
    PS: https://github.com/sekaijin/webapp.w...rd/tree/master

  2. #2
    Expert éminent
    Avatar de sekaijin
    Homme Profil pro
    Urbaniste
    Inscrit en
    Juillet 2004
    Messages
    4 205
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Urbaniste
    Secteur : Santé

    Informations forums :
    Inscription : Juillet 2004
    Messages : 4 205
    Points : 9 127
    Points
    9 127
    Par défaut
    le projet module-whiteboard
    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
    <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/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<parent>
    		<groupId>fr.sekaijin.osgi.web</groupId>
    		<artifactId>parent</artifactId>
    		<version>0.0.1</version>
    	</parent>
    	<artifactId>module-whiteboard</artifactId>
    	<packaging>bundle</packaging>
    	<dependencies>
    		<dependency>
    			<groupId>org.osgi</groupId>
    			<artifactId>org.osgi.core</artifactId>
    			<version>4.2.0</version>
    			<scope>provided</scope>
    		</dependency>
    		<dependency>
    			<groupId>org.osgi</groupId>
    			<artifactId>org.osgi.compendium</artifactId>
    			<version>4.2.0</version>
    			<scope>provided</scope>
    		</dependency>
    	</dependencies>
    	<build>
    		<resources>
    			<resource>
    				<directory>src/main/resources</directory>
    				<filtering>true</filtering>
    			</resource>
    		</resources>
    		<plugins>
    			<plugin>
    				<groupId>org.apache.felix</groupId>
    				<artifactId>maven-bundle-plugin</artifactId>
    				<version>3.2.0</version>
    				<extensions>true</extensions>
    				<configuration>
    					<instructions>
    						<Bundle-SymbolicName>${web.context}-${project.artifactId}</Bundle-SymbolicName>
    						<Bundle-Name>${project.name}</Bundle-Name>
    					</instructions>
    				</configuration>
    			</plugin>
    		</plugins>
    	</build>
    </project>
    Les classes suivantes servent à définir un tableau blanc abstrait qui pourra être utilisé par le main pour définir un tableau blanc partageant main-api
    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
    34
    35
    36
    37
    package fr.sekaijin.osgi.web.module.tracker;
     
    import org.osgi.framework.BundleContext;
    import org.osgi.util.tracker.ServiceTracker;
    import org.osgi.util.tracker.ServiceTrackerCustomizer;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
     
    public class ModuleTracker extends Thread {
    	private ServiceTracker tracker;
    	private ServiceTrackerCustomizer ModuleTrackerCustomizer;
    	private String className;
    	private Logger log;
    	private BundleContext context;
     
    	public ModuleTracker(BundleContext context, String className, ServiceTrackerCustomizer ModuleTrackerCustomizer) {
    		this.ModuleTrackerCustomizer = ModuleTrackerCustomizer;
    		this.className = className;
    		this.log = LoggerFactory.getLogger(getClass());
    		this.context = context;
    	}
     
    	public void run() {
    		try {
    			this.tracker = new ServiceTracker(context, className, ModuleTrackerCustomizer);
    			this.tracker.open();
    		} catch (Throwable e) {
    			log.error(e.getMessage(), e);
    		}
    	}
     
    	public synchronized void close() {
    		if (tracker != null) {
    			tracker.close();
    		}
    	}
    }
    Le module tracker va tourner dans un thread. Il fait appel à une implémentation de la classe suivante.
    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
    package fr.sekaijin.osgi.web.module.tracker;
     
    import org.osgi.framework.BundleContext;
    import org.slf4j.LoggerFactory;
     
    abstract public class AbstractModuleTrackerCustomizer {
     
    	protected final BundleContext context;
    	protected org.slf4j.Logger log;
     
    	public AbstractModuleTrackerCustomizer(BundleContext context) {
    		super();
    		this.context = context;
    		log = LoggerFactory.getLogger(getClass());
    	}
     
    }

    Nous entrons dans le vif du sujet. Le projet main définissant la webapp pour faire du bundle une webapp il suffit de mettre les attributs Webapp-Context et Web-ContextPath dans le manifest.
    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
    <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/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
     
    	<parent>
    		<groupId>fr.sekaijin.osgi.web</groupId>
    		<artifactId>modules</artifactId>
    		<version>0.0.1</version>
    	</parent>
     
    	<artifactId>main</artifactId>
    	<name>Sekaijin :: Web :: Module :: Main</name>
    	<packaging>bundle</packaging>
    	<properties>
    		<module.name>main</module.name>
    	</properties>
     
    	<dependencies>
    		<dependency>
    			<groupId>fr.sekaijin.osgi.web</groupId>
    			<artifactId>main-api</artifactId>
    			<version>0.0.1</version>
    			<scope>provided</scope>
    		</dependency>
    		<dependency>
    			<groupId>fr.sekaijin.osgi.web</groupId>
    			<artifactId>module-whiteboard</artifactId>
    			<version>0.0.1</version>
    			<scope>compile</scope>
    		</dependency>
    		<dependency>
    			<groupId>org.osgi</groupId>
    			<artifactId>org.osgi.core</artifactId>
    			<version>4.2.0</version>
    			<scope>compile</scope>
    		</dependency>
    		<dependency>
    			<groupId>org.osgi</groupId>
    			<artifactId>org.osgi.compendium</artifactId>
    			<version>4.2.0</version>
    			<scope>compile</scope>
    		</dependency>
    	</dependencies>
     
    	<build>
    		<resources>
    			<resource>
    				<directory>src/main/resources</directory>
    				<filtering>true</filtering>
    			</resource>
    		</resources>
    		<plugins>
    			<plugin>
    				<groupId>org.apache.felix</groupId>
    				<artifactId>maven-bundle-plugin</artifactId>
    				<version>3.2.0</version>
    				<extensions>true</extensions>
    				<configuration>
    					<instructions>
    						<Bundle-SymbolicName>${web.context}-${module.name}</Bundle-SymbolicName>
    						<Bundle-Name>${project.name}</Bundle-Name>
    						<Web-ContextPath>${web.context}</Web-ContextPath>
    						<Webapp-Context>${web.context}</Webapp-Context>
    					</instructions>
    				</configuration>
    			</plugin>
    		</plugins>
    	</build>
    </project>
    La définition du Customizer pour le tracker
    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
    34
    35
    36
    37
    package fr.sekaijin.osgi.web.main.tracker;
     
    import org.osgi.framework.BundleContext;
    import org.osgi.framework.ServiceReference;
    import org.osgi.util.tracker.ServiceTrackerCustomizer;
     
    import fr.sekaijin.osgi.web.main.api.IModule;
    import fr.sekaijin.osgi.web.main.registrar.MenuItemRegistrar;
    import fr.sekaijin.osgi.web.module.tracker.AbstractModuleTrackerCustomizer;
     
    public class ModuleTrackerCustomizer extends AbstractModuleTrackerCustomizer implements ServiceTrackerCustomizer {
    	public ModuleTrackerCustomizer(BundleContext context) {
    		super(context);
    	}
     
    	@Override
    	public void modifiedService(ServiceReference reference, Object service) {
    		removedService(reference, service);
    		addingService(reference);		
    	}
     
    	@Override
    	public Object addingService(ServiceReference reference) {
    		IModule service = IModule.class.cast(context.getService(reference));
    		log.info("ADDING MODULE    : " + service.getModuleName());
    		MenuItemRegistrar.addMenuItem(service.getModuleName(), service.getMenuItems());
    		return service;
    	}
     
    	@Override
    	public void removedService(ServiceReference reference, Object ser) {
    		IModule service = IModule.class.cast(context.getService(reference));
    		log.info("REMOVING MODULE  : " + service.getModuleName());
    		MenuItemRegistrar.removeMenuItem(service.getModuleName());
    	}
     
    }
    à chaque module qui s'enregistrer on ajoute ce menu dans un registre.
    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
    34
    35
    36
    37
    38
    package fr.sekaijin.osgi.web.main.registrar;
     
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
     
    import fr.sekaijin.osgi.web.main.api.MenuItem;
     
    public class MenuItemRegistrar {
    	private static Map<String, List<MenuItem>> MENU_ITEMS = new HashMap<>();
     
    	public static synchronized Map<String, List<MenuItem>> getMenuItems() {
    		return MENU_ITEMS;
    	}
     
    	public static synchronized void setMenuItems(Map<String, List<MenuItem>> menuItems) {
    		MENU_ITEMS = menuItems;
    	}
     
    	public static synchronized void addMenuItem(String module, List<MenuItem> menuItems) {
    		MENU_ITEMS.put(module, menuItems);
    	}
     
    	public static synchronized void addMenuItem(String module, MenuItem menuItem) {
    		if (MENU_ITEMS.containsKey(module)) {
    			MENU_ITEMS.get(MENU_ITEMS).add(menuItem);
    		} else {
    			List<MenuItem> tempMenuItemList = new ArrayList<>();
    			tempMenuItemList.add(menuItem);
    			MENU_ITEMS.put(module, tempMenuItemList);
    		}
    	}
     
    	public static synchronized void removeMenuItem(String module) {
    		MENU_ITEMS.remove(module);
    	}
    }
    une jsp placées dans le dossier registre utilise le registre
    Code jsp : 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
    <%@page import="fr.sekaijin.osgi.web.main.api.MenuItem"%>
    <%@page import="fr.sekaijin.osgi.web.main.registrar.MenuItemRegistrar"%>
    <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
    <title>Main Menu</title>
    </head>
    <body>
    	<ul>
    		<%
                            for (String module : MenuItemRegistrar.getMenuItems().keySet()) {
                                    for (MenuItem menuItem : MenuItemRegistrar.getMenuItems().get(module)) {
                    %><li><img src="<%=module%>/static/<%=menuItem.getIcon()%>" />
    			<a href="<%=module%>/<%=menuItem.getPath()%>"><%=menuItem.getModuleName()%>=><%=menuItem.getKey()%></a></li>
    		<%
                                    }
                            }%>
    	</ul>
    </body>
    </html>
    et enfin l'élément essentiel le fichier blueprint
    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
    <?xml version="1.0" encoding="UTF-8"?>
    <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0">
     
    	<bean class="fr.sekaijin.osgi.web.module.tracker.ModuleTracker"
    		init-method="start" destroy-method="close">
    		<argument type="org.osgi.framework.BundleContext" ref="blueprintBundleContext" />
    		<argument type="java.lang.String" value="fr.sekaijin.osgi.web.main.api.IModule" />
    		<argument type="org.osgi.util.tracker.ServiceTrackerCustomizer">
    			<bean class="fr.sekaijin.osgi.web.main.tracker.ModuleTrackerCustomizer">
    				<argument type="org.osgi.framework.BundleContext" ref="blueprintBundleContext" />
    			</bean>
    		</argument>
    	</bean>
     
    	<service id="welcomeFileService"
    		interface="org.ops4j.pax.web.extender.whiteboard.WelcomeFileMapping">
    		<bean
    			class="org.ops4j.pax.web.extender.whiteboard.runtime.DefaultWelcomeFileMapping">
    			<property name="redirect" value="false" />
    			<property name="welcomeFiles">
    				<array>
    					<value>index.jsp</value>
    					<value>index.html</value>
    				</array>
    			</property>
    		</bean>
    	</service>
     
    	<service interface="org.ops4j.pax.web.extender.whiteboard.ResourceMapping">
    		<bean
    			class="org.ops4j.pax.web.extender.whiteboard.runtime.DefaultResourceMapping">
    			<property name="alias" value="/${web.context}/${module.name}/static" /> <!-- http path -->
    			<property name="path" value="/local" /> <!--Local Folder within jar -->
    		</bean>
    	</service>
     
    </blueprint>
    le point essentiel ici est
    Code xml : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    	<bean class="fr.sekaijin.osgi.web.module.tracker.ModuleTracker"
    		init-method="start" destroy-method="close">
    		<argument type="org.osgi.framework.BundleContext" ref="blueprintBundleContext" />
    		<argument type="java.lang.String" value="fr.sekaijin.osgi.web.main.api.IModule" />
    		<argument type="org.osgi.util.tracker.ServiceTrackerCustomizer">
    			<bean class="fr.sekaijin.osgi.web.main.tracker.ModuleTrackerCustomizer">
    				<argument type="org.osgi.framework.BundleContext" ref="blueprintBundleContext" />
    			</bean>
    		</argument>
    	</bean>
    Ce bloc créer le thread tracker et l'exécute. Il utilise IModule et le ModuleTrackerCustomizer de main défini ci-dessus.
    À ce point tout nouveau module déclarant, un menu verra cette déclaration ajoutée au registre. C'est tout pour le Main.
    A+JYT
    PS: https://github.com/sekaijin/webapp.w...rd/tree/master

  3. #3
    Expert éminent
    Avatar de sekaijin
    Homme Profil pro
    Urbaniste
    Inscrit en
    Juillet 2004
    Messages
    4 205
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Urbaniste
    Secteur : Santé

    Informations forums :
    Inscription : Juillet 2004
    Messages : 4 205
    Points : 9 127
    Points
    9 127
    Par défaut
    Le module user.
    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
    <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/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<parent>
    		<groupId>fr.sekaijin.osgi.web</groupId>
    		<artifactId>modules</artifactId>
    		<version>0.0.1</version>
    	</parent>
    	<artifactId>user</artifactId>
    	<packaging>bundle</packaging>
    	<name>Sekaijin :: Web :: Module :: User</name>
    	<properties>
    		<module.name>user</module.name>
    	</properties>
     
    	<build>
    		<resources>
    			<resource>
    				<directory>src/main/resources</directory>
    				<filtering>true</filtering>
    			</resource>
    		</resources>
    		<plugins>
    			<plugin>
    				<groupId>org.apache.felix</groupId>
    				<artifactId>maven-bundle-plugin</artifactId>
    				<version>3.2.0</version>
    				<extensions>true</extensions>
    				<configuration>
    					<instructions>
    						<Bundle-SymbolicName>${web.context}-${module.name}</Bundle-SymbolicName>
    						<Bundle-Name>${project.name}</Bundle-Name>
    					</instructions>
    				</configuration>
    			</plugin>
    		</plugins>
    	</build>
    </project>
    On peut noter que dans le module rien dans le manifest ne concerne une webapp.
    Les deux servlets.
    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
    package fr.sekaijin.osgi.web.module.user.servlet;
     
    import java.io.IOException;
     
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
     
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
     
    public class Add extends HttpServlet{
     
    	private static final long serialVersionUID = 1L;
    	private Logger log;
     
    	public Add() {
    		log = LoggerFactory.getLogger(getClass());
    	}
     
    	@Override
    	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		resp.getWriter().println("User - Add - Servlet");
    		log.info("User - Add - Servlet");
    	}
    }
    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
    package fr.sekaijin.osgi.web.module.user.servlet;
     
    import java.io.IOException;
     
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
     
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
     
    public class Manage extends HttpServlet{
     
    	private static final long serialVersionUID = 1L;
    	private Logger log;
     
    	public Manage() {
    		log = LoggerFactory.getLogger(getClass());
    	}
     
    	@Override
    	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		log.info("User - Manage - Servlet");
    		resp.getWriter().println("User - Manage - Servlet");
    	}
    }
    La définition des menus du module
    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
    package fr.sekaijin.osgi.web.module.user;
     
    import java.util.ArrayList;
    import java.util.List;
     
    import fr.sekaijin.osgi.web.main.api.IModule;
    import fr.sekaijin.osgi.web.main.api.MenuItem;
     
    public class UserModule implements IModule{
     
    	protected String moduleName;
     
    	public UserModule(String moduleName) {
    		super();
    		this.moduleName = moduleName;
    	}
     
    	public String getModuleName() {
    		return moduleName;
    	}
     
    	public List<MenuItem> getMenuItems() {
    		List<MenuItem> menuItemList = new ArrayList<>();
     
    		menuItemList.add(new MenuItem(moduleName, "user_add.png", "add", "add"));
    		menuItemList.add(new MenuItem(moduleName, "users.jpeg", "manage", "manage"));
     
    		return menuItemList;
    	}
     
    }
    tout le reste se passe dans blueprint.
    la définition du service pour le menu
    Code xml : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    	<service interface="fr.sekaijin.osgi.web.main.api.IModule">
    		<bean class="fr.sekaijin.osgi.web.module.user.UserModule">
    			<argument type="java.lang.String" value="${module.name}" />
    		</bean>
    	</service>
    La déclaration des servlets dans pax whiteboard
    Code xml : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    	<service interface="javax.servlet.Servlet">
    		<service-properties>
    			<entry key="alias" value="/${web.context}/${module.name}/add" />
    		</service-properties>
    		<bean class="fr.sekaijin.osgi.web.module.user.servlet.Add" />
    	</service>
     
    	<service interface="javax.servlet.Servlet">
    		<service-properties>
    			<entry key="alias" value="/${web.context}/${module.name}/manage" />
    		</service-properties>
    		<bean class="fr.sekaijin.osgi.web.module.user.servlet.Manage" />
    	</service>
    Enfin l'accès au ressources statiques du module
    Code xml : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    	<service interface="org.ops4j.pax.web.extender.whiteboard.ResourceMapping">
    		<bean
    			class="org.ops4j.pax.web.extender.whiteboard.runtime.DefaultResourceMapping">
    			<property name="alias" value="/${web.context}/${module.name}/static" /> <!-- http path -->
    			<property name="path" value="/local" /> <!--Local Folder within jar -->
    		</bean>
    	</service>
    le fichier blueprint complet
    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
    <?xml version="1.0" encoding="UTF-8"?>
    <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0">
     
    	<service interface="fr.sekaijin.osgi.web.main.api.IModule">
    		<bean class="fr.sekaijin.osgi.web.module.user.UserModule">
    			<argument type="java.lang.String" value="${module.name}" />
    		</bean>
    	</service>
     
    	<service interface="javax.servlet.Servlet">
    		<service-properties>
    			<entry key="alias" value="/${web.context}/${module.name}/add" />
    		</service-properties>
    		<bean class="fr.sekaijin.osgi.web.module.user.servlet.Add" />
    	</service>
     
    	<service interface="javax.servlet.Servlet">
    		<service-properties>
    			<entry key="alias" value="/${web.context}/${module.name}/manage" />
    		</service-properties>
    		<bean class="fr.sekaijin.osgi.web.module.user.servlet.Manage" />
    	</service>
     
    	<service interface="org.ops4j.pax.web.extender.whiteboard.ResourceMapping">
    		<bean
    			class="org.ops4j.pax.web.extender.whiteboard.runtime.DefaultResourceMapping">
    			<property name="alias" value="/${web.context}/${module.name}/static" /> <!-- http path -->
    			<property name="path" value="/local" /> <!--Local Folder within jar -->
    		</bean>
    	</service>
    </blueprint>
    On constate que la déclaration des servlets ou du menu est très simple.
    Un point m'a posé un petit souci au départ c'est l'URL des alias. Il faut y placer le contextPah.
    Attention grosse correction sur ce point je vous conseille de lire le post suivant
    http://www.developpez.net/forums/d16...efile-problem/
    (je reviendrais sur l'ensemble pour poster une version améliorée)
    Pour le module message, il en va de même
    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
    <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/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<parent>
    		<groupId>fr.sekaijin.osgi.web</groupId>
    		<artifactId>modules</artifactId>
    		<version>0.0.1</version>
    	</parent>
    	<artifactId>message</artifactId>
    	<packaging>bundle</packaging>
    	<name>Sekaijin :: Web :: Module :: Message</name>
     
    	<build>
    		<resources>
    			<resource>
    				<directory>src/main/resources</directory>
    				<filtering>true</filtering>
    			</resource>
    		</resources>
    		<plugins>
    			<plugin>
    				<groupId>org.apache.felix</groupId>
    				<artifactId>maven-bundle-plugin</artifactId>
    				<version>3.2.0</version>
    				<extensions>true</extensions>
    				<configuration>
    					<instructions>
    						<Bundle-SymbolicName>${web.context}-${module.name}</Bundle-SymbolicName>
    						<Bundle-Name>${project.name}</Bundle-Name>
    					</instructions>
    				</configuration>
    			</plugin>
    		</plugins>
    	</build>
    	<properties>
    		<module.name>message</module.name>
    	</properties>
    </project>
    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
    package fr.sekaijin.osgi.web.module.message.servlet;
     
    import java.io.IOException;
     
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
     
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
     
    public class Create extends HttpServlet {
     
    	private static final long serialVersionUID = 1L;
    	private Logger log;
     
    	public Create() {
    		log = LoggerFactory.getLogger(getClass());
    	}
     
    	@Override
    	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		resp.getWriter().println("Message - Create - Servlet");
    		log.info("Message - Create - Servlet");
    	}
    }
    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
    package fr.sekaijin.osgi.web.module.message.servlet;
     
    import java.io.IOException;
     
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
     
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
     
    public class Outbox extends HttpServlet{
     
    	private static final long serialVersionUID = 1L;
    	private Logger log;
     
    	public Outbox() {
    		log = LoggerFactory.getLogger(getClass());
    	}
     
    	@Override
    	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		resp.getWriter().println("Message - Outbox - Servlet");
    		log.info("Message - Outbox - Servlet");
    	}
    }
    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
    package fr.sekaijin.osgi.web.module.message.servlet;
     
    import java.io.IOException;
     
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
     
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
     
    public class Sent extends HttpServlet{
     
    	private static final long serialVersionUID = 1L;
    	private Logger log;
     
    	public Sent() {
    		log = LoggerFactory.getLogger(getClass());
    	}
     
    	@Override
    	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		resp.getWriter().println("Message - Sent - Servlet");
    		log.info("Message - Sent - Servlet");
    	}
    }
    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
    package fr.sekaijin.osgi.web.module.message.servlet;
     
    import java.io.IOException;
     
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
     
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
     
    public class Inbox extends HttpServlet{
     
    	private static final long serialVersionUID = 1L;
    	private Logger log;
     
    	public Inbox() {
    		log = LoggerFactory.getLogger(getClass());
    	}
     
    	@Override
    	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    		resp.getWriter().println("Message - Inbox - Servlet");
    		log.info("Message - Inbox - Servlet");
    	}
    }
    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
    34
    package fr.sekaijin.osgi.web.module.message;
     
    import java.util.ArrayList;
    import java.util.List;
     
    import fr.sekaijin.osgi.web.main.api.IModule;
    import fr.sekaijin.osgi.web.main.api.MenuItem;
     
    public class MessageModule 
    implements IModule {
     
    	protected String moduleName;
     
    	public MessageModule(String moduleName) {
    		super();
    		this.moduleName = moduleName;
    	}
     
    	public String getModuleName() {
    		return moduleName;
    	}
     
    	public List<MenuItem> getMenuItems() {
    		List<MenuItem> menuItemList = new ArrayList<>();
     
    		menuItemList.add(new MenuItem(moduleName, "add.png", "create", "create"));
    		menuItemList.add(new MenuItem(moduleName, "inbox.png", "inbox", "inbox"));
    		menuItemList.add(new MenuItem(moduleName, "outbox.png", "outbox", "outbox"));
    		menuItemList.add(new MenuItem(moduleName, "sent.png", "sent", "sent"));
     
    		return menuItemList;
    	}
     
    }
    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
    <?xml version="1.0" encoding="UTF-8"?>
    <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0">
     
    	<service interface="fr.sekaijin.osgi.web.main.api.IModule">
    		<bean class="fr.sekaijin.osgi.web.module.message.MessageModule">
    			<argument type="java.lang.String" value="${module.name}" />
    		</bean>
    	</service>
     
    	<service interface="javax.servlet.Servlet">
    		<service-properties>
    			<entry key="alias" value="/${web.context}/${module.name}/create" />
    		</service-properties>
    		<bean class="fr.sekaijin.osgi.web.module.message.servlet.Create" />
    	</service>
     
    	<service interface="javax.servlet.Servlet">
    		<service-properties>
    			<entry key="alias" value="/${web.context}/${module.name}/inbox" />
    		</service-properties>
    		<bean class="fr.sekaijin.osgi.web.module.message.servlet.Inbox" />
    	</service>
     
    	<service interface="javax.servlet.Servlet">
    		<service-properties>
    			<entry key="alias" value="/${web.context}/${module.name}/outbox" />
    		</service-properties>
    		<bean class="fr.sekaijin.osgi.web.module.message.servlet.Outbox" />
    	</service>
     
    	<service interface="javax.servlet.Servlet">
    		<service-properties>
    			<entry key="alias" value="/${web.context}/${module.name}/sent" />
    		</service-properties>
    		<bean class="fr.sekaijin.osgi.web.module.message.servlet.Sent" />
    	</service>
     
    	<service interface="org.ops4j.pax.web.extender.whiteboard.ResourceMapping">
    		<bean
    			class="org.ops4j.pax.web.extender.whiteboard.runtime.DefaultResourceMapping">
    			<property name="alias" value="/${web.context}/${module.name}/static" /> <!-- http path -->
    			<property name="path" value="/local" /> <!--Local Folder within jar -->
    		</bean>
    	</service>
    </blueprint>

    Voilà. une fois main-api, module-whiteboard et main déployés l'accès à l'appli se fait via l'URL http://localhost:8181/osgi/index.jsp
    À par le titre cela produit une page blanche. le déploiement du module user et les items user add et user manage apparaissent
    De même avec le module messages. L'arrêt d'un des modules fait disparaitre le menu correspondant.

    A+JYT
    PS: https://github.com/sekaijin/webapp.w...rd/tree/master

  4. #4
    Expert éminent
    Avatar de sekaijin
    Homme Profil pro
    Urbaniste
    Inscrit en
    Juillet 2004
    Messages
    4 205
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Urbaniste
    Secteur : Santé

    Informations forums :
    Inscription : Juillet 2004
    Messages : 4 205
    Points : 9 127
    Points
    9 127
    Par défaut
    Comme dit dans les précédent posts j'ai corrigé le code pour utiliser correctement le web-contextPath de pax web.

    J'au aussi simplifié le module tracker (il se réduite à une classe)
    J'ai tout mis sur github avec un bout de doc
    https://github.com/sekaijin/webapp.whiteboard/

    A+JYT

  5. #5
    Expert éminent
    Avatar de sekaijin
    Homme Profil pro
    Urbaniste
    Inscrit en
    Juillet 2004
    Messages
    4 205
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 60
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Urbaniste
    Secteur : Santé

    Informations forums :
    Inscription : Juillet 2004
    Messages : 4 205
    Points : 9 127
    Points
    9 127
    Par défaut
    Bonjour,

    J'ai ajouté webix pour générer et afficher le menu en javascript.
    Vient ensuite l'ajout de une websocket.
    lorsque un module est démarré ou arrêté le moduleTracker ajoute ou enlève la définition du registre puis notifie les client via la websocket.
    Le js recharge alors le menu.

    A+JYT

Discussions similaires

  1. Spring OSGi Web App - IHM modulaire
    Par zaewonyx dans le forum OGSi
    Réponses: 2
    Dernier message: 28/05/2013, 10h00
  2. [Tomcat] Installation de la web-app admin sous windows
    Par gondek dans le forum Tomcat et TomEE
    Réponses: 2
    Dernier message: 09/01/2006, 22h55
  3. [Web-App][Servlet][Axis] Problème lors de l'arrêt de la web-app
    Par c.tranquille dans le forum Tomcat et TomEE
    Réponses: 1
    Dernier message: 02/12/2005, 09h49
  4. Tomcat 5.5 ( gestion des privilèges d'une web app )
    Par mick72 dans le forum Tomcat et TomEE
    Réponses: 1
    Dernier message: 03/09/2005, 06h54
  5. [CR9] [D2005] [ASP.NET WEB App] Création dynamique d'un état
    Par Crystalou dans le forum SAP Crystal Reports
    Réponses: 5
    Dernier message: 09/03/2005, 09h21

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo