Modularisierbare Web-Anwendungen mit JEE6 Web Profile web-fragments

Thomas Darimont

Erfahrenes Mitglied
Hallo,

hier mal ein kleines Beispiel für modulare Webanwendungen mit JEE6 Web Profile und web-fragments.
web-fragments sind eine Erweiterung im Java EE 6 Web Profile, die es ermöglichen Web-Anwendungen aus einzelnen Teilen (wie etwa Servlet-, Filter- Definitionen, Ressourcen (Bilder, jsp, js, css, html) zusammensetzen zu können.
Eine gute Übersicht bietet beispielsweise 2).

Dieses Konzept möchte ich nun mit diesem Artikel vorstellen.

Dazu skizziere ich eine kleine Webanwendung, welche aus 3 Modulen (module1, module2,module3)
und einem Untermodul (module3.sub1) besteht. Das komplette Beispiel-Setting findet man im Anhang

Für das Beispiel verwende ich die Spring Source Tool Suite (STS) 2.5.2, maven 2.2.x, Java EE 6 Web Profile
und Tomcat 7.0.8

Um die Konfiguration zu vereinfachen legen wir uns ein Maven Projekt mit dem Namen de.tutorials.build
und packaging pom an. Dieses Projekt dient als parent-Projekt für alle weiteren Projekte.

pom.xml (de.tutorials.build)
XML:
<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>de.tutorials</groupId>
	<artifactId>de.tutorials.build</artifactId>
	<version>3.0.0-SNAPSHOT</version>
	<packaging>pom</packaging>

	<dependencies>
		<dependency>
			<groupId>javax</groupId>
			<artifactId>javaee-web-api</artifactId>
			<version>6.0</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>

	<repositories>
		<repository>
			<id>java.net2</id>
			<name>Repository hosting the jee6 artifacts</name>
			<url>http://download.java.net/maven/2</url>
		</repository>
	</repositories>
</project>

Anstatt die Abhängigkeiten zum Servlet API 3.0 etc. manuell zu definieren geben wir hier einfach
das javaee-web-api als dependency an.


Anschließend legen wir mit Maven eine web-app (de.tutorials.web.app) an.

Diese web-app ist unsere Basis Anwendungen die wir mit Modulen ausbauen werden.

pom.xml (de.tutorials.web.app)
XML:
<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>de.tutorials</groupId>
	<artifactId>de.tutorials.web.app</artifactId>
	<packaging>war</packaging>
	<version>3.0.0-SNAPSHOT</version>
	<name>de.tutorials.web.app</name>
	<url>http://maven.apache.org</url>

	<parent>
		<groupId>de.tutorials</groupId>
		<artifactId>de.tutorials.build</artifactId>
		<version>3.0.0-SNAPSHOT</version>
		<relativePath>../de.tutorials.build</relativePath>
	</parent>

	<dependencies>

		 
	</dependencies>
	<build>
		<finalName>de.tutorials.web.app</finalName>
	</build>
</project>
Wie man sieht wird das parent-Projekt über einen relativen Pfad referenziert... am besten legt
man die Web App mit dem m2eclipse Plugin erstmal ohne die Angabe eines Parent-Projektes an und
fügt die Angabe dann später von Hand im pom.xml hinzu.


Die web.xml unseres web-app Projektes definieren wir so:

web.xml (de.tutorials.web.app)
XML:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
      version="3.0" metadata-complete="false">
  <display-name>de.tutorials.web.app</display-name>
</web-app>

Durch das "metadata-complete" Attribute können wir definieren, ob wir eine erweiterbare web-app definieren oder nicht.
Mit metadata-complete=false kennzeichnen wir die web-app als erweiterbar.

In der web-app definieren wir unter src/main/webapp folgende index.jsp:
HTML:
<%@page import="de.tutorials.web.app.modules.ModuleLister"%>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
	pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<body>
<h1>Modules</h1>

<ul>
<%for (String moduleId : ModuleLister.getModules(getServletContext())){%>
	<li><a href="<%=moduleId%>.jsp"><%=moduleId%></a></li>
<%}%>
</ul>

</body>
</html>

Hier listen wir unsere Fragmente bzw. "module" auf.
Das Auflisten der web-fragments führen wir mit der Klasse ModuleLister durch:

ModuleLister:
Java:
package de.tutorials.web.app.modules;

import java.net.URL;
import java.util.Collections;
import java.util.Set;
import java.util.TreeSet;

import javax.servlet.ServletContext;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;

public class ModuleLister {
	public static Set<String> getModules(ServletContext sc) throws Exception {
		Set<String> set = new TreeSet<String>();
		for (URL webFragmentUrl : Collections.list(sc.getClassLoader().getResources("META-INF/web-fragment.xml"))) {
			String fragmentId = extractFragmentId(webFragmentUrl);
			set.add(fragmentId);
		}
		return set;
	}

	private static String extractFragmentId(URL webFragmentUrl)
			throws Exception {
		Document d = DocumentBuilderFactory.newInstance().newDocumentBuilder()
				.parse(webFragmentUrl.toURI().toString());
		String fragmentId = d.getDocumentElement().getAttribute("id");
		return fragmentId;
	}
}

Da nun unsere Infrastruktur steht fügen wir nun ein paar Module hinzu.
Dazu erstellen wir 4 Maven Java Projekte (simple-project) mit bei diesen
Projekten stellen wir jeweils die Execution Environment (Java Build Path -> Libraries -> JRE -> Edit)
auf JSE 1.6

pom.xml de.tutorials.app.module1:
XML:
<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>de.tutorials</groupId>
  <artifactId>de.tutorials.app.module1</artifactId>
  <version>3.0.0-SNAPSHOT</version>
	<parent>
		<groupId>de.tutorials</groupId>
		<artifactId>de.tutorials.build</artifactId>
		<version>3.0.0-SNAPSHOT</version>
		<relativePath>../de.tutorials.build</relativePath>
	</parent>
</project>

Die Projekte in den Modul Projekten legen wir im Verzeichnis src/main/resources ein Verzeichnis META-INF an.
In META-INF legen wir eine Datei namens web-fragment.xml mit folgendem Inhalt an:

web-fragment.xml (de.tutorials.app.module1)
XML:
<web-fragment xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"
      version="3.0"
      id="module1">
</web-fragment>
Damit deklarieren wir das Projekt als Web-Fragment Projekt mit der Id "module1",.
Danach legen wir unter META-INF ein Verzeichnis mit dem Namen resources an.

Dort legen wir die Datei module1.jsp mit folgendem Inhalt an:

module1.jsp (de.tutorials.app.module1)
HTML:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
	pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Module 1</title>
</head>
<body>
Module 1
</body>
</html>

Wenn wir nun in unserer Web-App das module1 verwenden möchten, so definieren wir in der pom.xml (de.tutorials.web.app)
im Abschnitt dependencies die Abhängigkeit zu unserem module1-Projekt:

pom.xml (de.tutorials.web.app mit modul1)
XML:
<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>de.tutorials</groupId>
	<artifactId>de.tutorials.web.app</artifactId>
	<packaging>war</packaging>
	<version>3.0.0-SNAPSHOT</version>
	<name>de.tutorials.web.app</name>
	<url>http://maven.apache.org</url>

	<parent>
		<groupId>de.tutorials</groupId>
		<artifactId>de.tutorials.build</artifactId>
		<version>3.0.0-SNAPSHOT</version>
		<relativePath>../de.tutorials.build</relativePath>
	</parent>

	<dependencies>

		<dependency>
			<groupId>de.tutorials</groupId>
			<artifactId>de.tutorials.app.module1</artifactId>
			<version>3.0.0-SNAPSHOT</version>
		</dependency>
 
	</dependencies>
	<build>
		<finalName>de.tutorials.web.app</finalName>
	</build>
</project>

Startet man nun die web-app mit Tomcat und ruft die index.jsp Seite auf so sieht man dass unter Modules
unser module1 erscheint.

Definiert man die restlichen Module so erhält man am Ende folgende Ausgabe der index.jsp:
HTML:
<!DOCTYPE html>
<html>
<body>
<h1>Modules</h1>
<ul>
	<li><a href="module1.jsp">module1</a></li>
	<li><a href="module2.jsp">module2</a></li>
	<li><a href="module3.jsp">module3</a></li>
	<li><a href="module3.sub1.jsp">module3.sub1</a></li>
</ul>
</body>
</html>

Der web-fragments Mechanismus ist sehr flexibel. So kann ein Modul durch Abhängigkeiten zu weiteren Modulen auch
deren web-fragments auflesen.

Im angehängten Beispiel hat man beispielsweise folgende Abhängigkeitsstruktur:
de.tutorials.web.app -> de.tutorials.app.module3 -> de.tutorials.app.module3.sub1

Dabei definieren die Module de.tutorials.app.module3 und auch de.tutorials.app.module3.sub1 web-fragments
welche zur Laufzeit im Servlet Container verwendet werden.

Mit diesem Mechainsmus kann man sehr einfach erweiterbare Web-Anwendungen entwerfen. Verwendet man viele
Module die über web-fragments Komponenten zur web-app beisteuern so kann es wichtig sein die Reihenfolge
in der diese web-fragments ausgewertet werden zu bestimmen. Dazu gibt es mehrere Möglichkeiten siehe 1).
Natürlich kann man sich bei diesem Ansatz auch sehr leicht in den Fuß schießen... beispielsweise könnte durch das hinzufügen eines "fremden" jars plötzlich ein neues Servlet aktiv werden... hier ist einige Vorsicht geboten, jedoch existieren auch einige Mechanismen zur Kontrollierten Verwendung von web-fragments bereit (explizite Nennung der erlaubten Fragment Ids, bestimmte Servlets lassen sich deaktiveren etc. siehe 2) )

Weitere Ressourcen:
1) http://blogs.sun.com/swchan/entry/servlet_3_0_web_fragment
2) http://www.linuxtag.org/2010/fileadmin/www.linuxtag.org/slides/Peter Rossbach - Apache Tomcat 7.pdf

Gruß Tom
 

Anhänge

  • de.tutorials.training.web.modules.zip
    27,5 KB · Aufrufe: 195
Zuletzt bearbeitet von einem Moderator:
Zurück