Sonntag, 24. Februar 2013

K2.3 - HTML5 WebSocket Server mit Tomcat 7

Einleitung

In diesem Artikel werde ich zeigen, wie man die HTML5 WebSockets auf der Client-side und Server-side nutzt. Die WebSockets benötigen auf beiden Seiten spezielle APIs bzw. Implementierungen und gerade letztere gibt es in einigen Sprachen die im Web Umfeld relevant sind, z.B. Java, PHP, .NET, Ruby, Python, etc.

Heute werden wir auf der Client-side die neue HTML5 Javascript WebSocket API einsetzen und damit eine persistente Verbindung zum Webserver aufbauen. Außerdem werden wir eine Textnachricht zum Server senden und sogar eine Broadcast Nachricht vom Webserver empfangen. Der Client-side Code ist überwiegend von jcavallotti.blogspot.de kopiert - blogspot Blogger müssen schließlich zusammenhalten.

Auf der Server-side werden wir den Tomcat 7 nutzen mit seiner neuen Servlet API für WebSockets. Meine Hauptquelle für diesen Artikel war ein Tutorial auf tomcatexpert.com. Ich arbeite auf der Server-side bevorzugt mit Java oder PHP, daher kommen für mich Technologien wie Node.js oder Tornado nicht in Frage. Es gibt auch schon erste inoffizielle Module für den Apache http Server in C, die den Apache Webserver WebSocket-fähig machen. Was daraus wird bleibt abzuwarten.

Motivation

Mit einer persistenten Verbindung zum Server kann man eine Menge nützlicher Anwendungen umsetzen, allen voran sind das Applikationen mit Echtzeit Anforderungen. Mit Ajax ist lediglich ein s.g. Polling möglich, was einen extremen Overhead an unnötigen Verbindungsauf- und abbau bedeutet und somit die Anwendung einiges an Performance kostet.

Ein weiterer Vorteil von WebSockets ist, dass nun server-controlled Peer2Peer Verbindungen möglich sind. Das bedeutet, dass man zwar keine direkte Verbindung von Client zu Client hinbekommt, aber die Server-side Applikation die Verbindungen verwalten kann, und somit Daten direkt an Clients adressieren kann. Als bestes Beispiel dient hier ein Private Messaging Feature eines Chatsystems. Mit Ajax muss der Client zyklisch den Server fragen (polling), ob neue Private Nachrichten eingetroffen sind. Das verlangt außerdem einen Mehraufwand in der Programmierung bzgl. Sicherheit und Authentifizierung.
Mit den WebSockets benachrichtigt der Server den Client quasi in Echtzeit, wenn ein anderer Client eine Nachricht an diesen Client gesendet hat. Dieses Verfahren nennt man auch Event-basierte Kommunikation, weil der Client auf ein Ereignis reagiert, statt ständig nachzufragen, ob es ein Ereignis gibt.

WebSockets sind ein echtes Highlight im HTML5 Standard. Wer sich jetzt damit vertraut macht kann bestehende Anwendungen optimieren oder ganz neue state-of-the-art Applikationen bauen, wie z.B. tweetping.net.

Voraussetzungen

 Wir benötigen folgende Tools:
  • Eclipse
  • Maven
  • Tomcat 7
  • HTML5 kompatiblen Browser
Ich empfehle außerdem die Tomcat 7 Websocket API Dokumentation. Alle hier genannten Voraussetzungen werden in vorherigen Artikeln (K2.1 bis K2.2.1) ausführlich behandelt.

Umsetzung

Kurzübersicht aller Schritte

  1. Neue Verzeichnisstruktur inklusive Dateien im parentproject anlegen
  2. Verzeichnis als Maven Projekt importieren
  3. Eclipse Run Configuration einrichten
  4. Tomcat 7 Server starten
  5. Maven Cargo Plugin ausführen
  6. Mit HTML websocket Client verbinden
Den gesamten Code des aktuellen Standes meines Maven Projekts parentproject gibt es hier zum Download.

1. Verzeichnisstruktur anlegen

Wir wollen folgende Verzeichnisse und Dateien anlegen:


Die Quelltexte zu den 3 Dateien sehen folgendermaßen aus:

pom.xml

<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelversion>4.0.0</modelversion>
   <artifactid>websocket</artifactid>
   <version>1.0.0</version>
   <packaging>war</packaging>
  
   <parent>
    <groupid>de.blogspot.sw-technik</groupid>
     <artifactid>parentproject</artifactid>
     <version>0.0.1-SNAPSHOT</version>
   </parent>

 <properties>
  <tomcat .version="">7.0.29</tomcat>
  <cargo .version="">1.2.4</cargo>
 </properties>

 <build>
  <plugins>
   <plugin>
    <groupid>org.codehaus.cargo</groupid>
    <artifactid>cargo-maven2-plugin</artifactid>
    <version>${cargo.version}</version>
    <!-- Cargo Tomcat --> 
    <configuration>
     <container>
      <containerid>tomcat7x</containerid>
      <type>remote</type>
     </container>
     <configuration>
      <type>runtime</type>
      <properties>
       <!-- /text requires the tomcat-users.xml user/role config with manager-script -->
       <cargo .tomcat.manager.url="">http://localhost:8080/manager/text</cargo>
       <cargo .remote.username="">tomcat</cargo>
       <cargo .remote.password="">tomcat</cargo>
      </properties>
     </configuration>
    </configuration>
    <dependencies>
     <dependency>
        <groupid>org.codehaus.cargo</groupid>
        <artifactid>cargo-core-container-tomcat</artifactid>
        <version>${cargo.version}</version>
     </dependency>
    </dependencies>
   </plugin>
  </plugins>
 </build>
   
 <dependencies>

  <dependency>
   <groupid>org.apache.tomcat</groupid>
   <artifactid>tomcat-catalina</artifactid>
   <version>${tomcat.version}</version>
   <scope>provided</scope>
  </dependency>
  
  <dependency>
   <groupid>org.apache.tomcat</groupid>
   <artifactid>tomcat-coyote</artifactid>
   <version>${tomcat.version}</version>
   <scope>provided</scope>
  </dependency>
  
 </dependencies>
 
</project>


web.xml

<web-app>
   <display-name>Archetype Created Web Application</display-name>
 <servlet>
  <servlet-name>ws_servlet</servlet-name>
  <servlet-class>de.blogspot.swtechnik.servlet.WsServlet</servlet-class>
 </servlet>
 <servlet-mapping>
  <servlet-name>ws_servlet</servlet-name>
  <url-pattern>/websocket</url-pattern>
 </servlet-mapping>
</web-app>

de.blogspot.swtechnik.servlet.WsServlet.java

package de.blogspot.swtechnik.servlet;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import javax.servlet.http.HttpServletRequest;

import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;
import org.apache.catalina.websocket.WsOutbound;

public class WsServlet extends WebSocketServlet {

 private static final long serialVersionUID = 4541321228711151433L;

 /**
  * Connected clients
  */
 private static List clients = new ArrayList();
 
 /**
  * Send a message to all clients connected.
  * 
  * @param message
  */
 private void broadcast(String message) {
  StreamInbound someClient;
  ListIterator iter = clients.listIterator();
  while (iter.hasNext()) {
   someClient = (MessageInbound) iter.next();
   try {
    someClient.getWsOutbound().writeTextMessage(
      CharBuffer.wrap(message)
     );
   } catch (IOException e) {
   }
  }
 }
 
 @Override
 protected StreamInbound createWebSocketInbound(String string, HttpServletRequest hsr) {
  MessageInbound inbound = new MessageInbound() {
   
   @Override
   protected void onClose(int status) {
    System.out.println("onClose - status code: " + status);
    clients.remove(this);
   }
   
   @Override
   protected void onBinaryMessage(ByteBuffer bb) throws IOException {
    System.out.println("onBinaryMessage");
   }

   @Override
   protected void onTextMessage(CharBuffer cb) throws IOException {
    System.out.println("onTextMessage");
    
    CharBuffer msg = CharBuffer.wrap(cb);
    WsOutbound outbound = getWsOutbound();
    // Send message to client connected
    outbound.writeTextMessage(msg);
    // Send message to all clients connected
    broadcast("Broadcast");
   }
   
   @Override
   protected void onOpen(WsOutbound outbound) {
    int connSize = clients.size();
    System.out.println("onOpen - connections: " + connSize);
   }
  };
  
  // Collect clients connected
  clients.add(inbound);
  
  return inbound;
 }
}

2. Import als Maven Projekt

Öffnen Sie in Eclipse die Import View:
Nach Abschluss dieses Dialogs sollten Sie nun ein eigenständiges Projekt in Eclipse haben.


3. Eclipse Run Configuration einrichten

Für dieses Projekt nutzen wir das Maven Cargo Plugin, um unsere Web Anwendung per Klick auf dem Tomcat Server zu deployen.


4. Tomcat 7 starten

Für HTML5 websockets wird ein Server benötigt, der das ws und wss Protokoll implementiert. Der Apache http Webserver ist momentan noch nicht fähig Verbindungen mit diesem Protokoll zu verwalten. Aktuelle Server, die websockets unterstützen sind z.B.
Wir werden in diesem Tutorial den Tomcat 7 verwenden, da es mir lieber ist auf der Server-side mit Java, statt Javascript zu programmieren. Sicherlich sind die Java Lösungen nicht so performant, wie ein Node.js, aber für die meisten Anwendungen wird ein Tomcat 7 sicher mehr als ausreichend sein.

Tomcat 7 sollte in Eclipse bereits als Server eingerichtet sein. Nun starten Sie den Server:

5. Maven Cargo Plugin ausführen

In der pom.xml haben wir das Cargo Plugin integriert. Außerdme haben wir die Run Configuration eingerichtet und wenn wir nun das Projekt auf dem laufenden Tomcat Server deployen möchten müssen wir nur diese Konfiguration ausführen.
Sie sollten in der Eclipse Konsole sehen wie das Projekt gebaut wird und direkt nach dem erfolgreichen Build den Deployment Pfad in Tomcat:

6. Websocket Client

Es gibt für Websockets nicht nur auf der Server-side spezielle Voraussetzungen, sondern natürlich auch auf der Client-side. Wir werden eine einfache HTML Seite mit Javascript Code als Client verwenden. Es existieren zahlreiche Websocket Client APIs in verschiedenen Sprachen, z.B. Java, Javascript, PHP, Ruby, Python, etc.
HTML Beispiel:
<!DOCTYPE html>  
<html>  
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.jquery.com/jquery-migrate-1.1.1.min.js"></script>  
<script type="text/javascript">
var ws = new WebSocket("ws://localhost:8080/websocket-1.0.0/websocket");
ws.onopen = function() {
  console.log("Websocket Ready!!");
}
ws.onclose = function() {
  console.log("Websocket Closed!!");
}
ws.onerror = function() {
  console.log("Websocket Error!!");
}
ws.onmessage= function(data) {
  console.log(data.data);
  if ("Hi!" == data.data) {
    //ws.close();
  }
}
function sendMessage() {
  ws.send(2);
}

$( document ).ready( function() {
  $("a").click(function( event ) {
    sendMessage();
  });
});
</script>  
<title>WebSockets Client</title>  
</head>  
<body>
<a href="#">LINK</a>
</body></html>


Speichern Sie den obigen HTML Code in einer .html Datei auf ihrem Rechner. Rufen Sie diese Datei im Browser auf, z.B.
Öffnen Sie die Entwicklertools des Browsers. Sie sollten dann eine Meldung "Websocket Ready!!" sehen, und in der Eclipse Konsole (rechts) sehen Sie die Ausgabe aus der Methode onClose(int status) aus der Servlet Klasse.

Klicken Sie nun einmal auf "LINK" und beobachten Sie die Eclipse und Browser Konsole. Hier die Veränderungen:
Und ein weiterer Klick ergibt folgende Ausgabe:
Nun wollen wir uns mit einem zweiten Client verbinden. Dafür müssen wir nur ein neues Tab öffnen und die HTML websocket Client Datei aufrufen:
Jetzt wollen wir die Broadcast Methode unseres Servlets einmal testen. Ein Broadcast ist eine Nachricht, die an alle Clients versendet wird. In unserem Fall werden wir per Klick auf "LINK" im ersten Tab den Broadcast quasi auslösen. Die Nachricht wird dann auch im zweiten Tab zu sehen sein, weder dass wir dort eine Aktion ausgeführt haben, noch muss das Tab oder der Browser dabei im Vordergrund sein.
Außerdem sehen Sie in der Eclipse Konsole, dass nun tatsächlich 2 Verbindungen existieren.
An dieser Stelle endet das Tutorial zu HTML5 WebSockets, allerdings ist damit das Thema noch lange nicht ausgeschöpft. Sie haben aber jetzt das nötige Basiswissen, um zu erkennen, welches Potential in WebSockets steckt und wie Sie die neuen Möglichkeiten für Ihre Anwendungen nutzen können.

Zusammenfassung

Sie haben gelernt, wie man den Tomcat 7 als WebSocket Server einsetzen kann und wie Sie sich mit einem Javascript Client verbinden können. Die lokale Entwicklungsumgebung wurde mit Eclipse und dem Maven Cargo Plugin praxisorientiert aufgesetzt, sodass Sie nun alles haben, um professionell moderne HTML5 Anwendungen entwickeln zu können.

Weitere Schritte

Dieser Artikel hat das Thema WebSockets nur in einem minimalen Umfang angerissen. Es lohnt sich weiter mit den verschiedenen APIs, Frameworks und Anwendungsfällen zu beschäftigen. Am besten in der jeweils bevorzugten Technologie für Client und Server.

Weiterführende Links zu diesem Thema:

Dienstag, 12. Februar 2013

K2.2.1 - Entwicklung im Team mit Apache Archiva, Maven, Git und Eclipse

Einleitung

In diesem Artikel werden wir zu aller erst Apache Archiva als zentralen Repository Management Server für ein Projekt konfigurieren, welches wir mit dem Magnolia CMS aufsetzen werden. Wir nutzen Git als Versionskontrollsystem und werden dafür auch ein zentrales Repository einrichten, ein s.g. Git bare repository.

Wir werden es uns nicht zu einfach machen und direkt ein Maven Projekt mit Untermodulen (Maven submodules) aufsetzen. Es gibt als Alternative noch die flache Projektstruktur, die ich persönlich nicht empfehle, da man für jedes Modul/Projekt ein eigenes Git Repository benötigen würde. Das kann dann etwas aufwendig werden in der Wartung und Verwaltung der Repositories. Ansonsten ist die flache Struktur nicht unbedingt schlechter, als die hierarchische. Man sollte sich die Vor- und Nachteile mal genauer ansehen, und dazu kann ich den Artikel von Torsten Horn empfehlen.

Außerdem zeige ich diverse Wege, wie man in Eclipse ein Maven Webapp Projekt auf einem Apache Tomcat deployen kann. Den Tomcat Server werden wir natürlich auch gemeinsam installieren und in Eclipse in der Servers View einrichten. Wir werden für das Deployment das Maven Cargo Plugin nutzen und dabei ist es übrigens egal, ob der Tomcat Server lokal oder entfernt (remote) läuft, da mit dem Plugin auf einen beliebigen Server deployed werden kann. Wir nutzen hier zwar Tomcat, aber das Plugin kann auch mit JBoss oder anderen Application Servern umgehen.

Am Ende dieses Artikels haben Sie dann ein Magnolia Webapp Projekt als Maven submodule in einem Maven parent Projekt unter Git Versionskontrolle. Mit den beiden zentralen Repository Servern für Maven und Git ist das eine solide Basis für die Entwicklung auch in einem größeren Team.

Motivation


Im Unternehmensumfeld macht es Sinn aus Gründen einer wartbaren und standardisierten Infrastuktur für die Softwareentwicklung einen zentralen Repository Server, sowohl für Maven als auch für Git aufzusetzen. Beide Technologien sind seit einiger Zeit dabei die etablierten Tools Apache Ant und Subversion abzulösen und das wohl zu recht. Aus eigener Erfahrung muss ich nach langjähriger Nutzung von SVN zugeben, dass Git als verteiltes Versionskontrollsystem einige Vorteile hat. Allerdings kenne ich keines der anderen Distributed Version Control Systems (DVCS), wie z.B. Mercurial. Wer etwas im Internet unterwegs ist und diverse Fachmagazine liest, der weiß, dass Git in diesen Medien eine enorme Präsenz hat und daher wohl einen größeren Bekanntheitsgrad genießt.

Auch Apache Maven kann einen ähnlichen Erfolg vorweisen. Der Umstieg von Ant auf Maven ist zwar etwas aufwendig, da die Workflows und die Projektstruktur komplett unterschiedlich sind, aber wer in die Zukunft investieren möchte, sollte die Migration auf jeden Fall angehen. Die Ant Experten und auch die SVN Gurus werden über die nächsten Jahre weniger werden, so dass Unternehmen es schwer haben werden fähiges Personal für ihre Projekte zu finden, wenn diese auf veralteten Technologien basieren. Und andersherum erhöht man natürlich als Entwickler seinen eigenen Markwert, wenn man moderne Technologien beherrscht.

Apropos "moderne Technologie", wie bereits erwähnt werden wir als erstes Maven Projekt eine Java Web Applikation mit dem Magnolia CMS aufsetzen. Magnolia gehört zweifellos zu den modernsten Content Management Systemen im Java Umfeld. Leider ist in diesem Artikel kein Platz mehr für eine Einführung in Magnolia, wer aber Interesse hat kann im Anschluss mit der sehr guten Dokumentation weiter arbeiten und das Projekt ausbauen. Ich empfehle zuerst mit der Webapp und Modul Dokumentation anzufangen.

Have fun!

Voraussetzungen

So wie es die Kapitelnummer im Titel dieses Artikels vermuten lässt, sollte man den vorherigen Artikel K2.2 - Professionelle Entwicklung mit Git und Maven unter Eclipse und mit Apache Archiva durchgearbeitet haben, um alle notwendigen Installationen bereit zu haben, z.B. eine lokal laufende virtuelle Maschine mit VirtualBox, um die Server Repositories für Maven und Git möglichst praxisnah betreiben zu können. Vorallem aber wird von Apache Archiva eine Installation  aufgesetzt, die in diesem Tutorial weiter konfiguriert wird und auch Eclipse mit den notwendigen Plugins m2eclipse und EGit werden installiert.

Server-Side (Linux Debian)


Workstation PC (Windows 7)


Umsetzung

Kurzübersicht aller Schritte

  1. Apache Archiva konfigurieren (Magnolia Remote Repository hinzufügen) (~5min)
  2. In Eclipse ein Maven Basis Projekt erstellen (ein s.g. parent module einer multi-module Struktur) (~5min)
  3. Magnolia Webapp als Projekt erstellen (als s.g. Maven submodule im Basis Projekt) (~5min)
  4. Apache Tomcat in Eclipse integrieren (~10min)
  5. Magnolia Webapp Maven Projekt auf Tomcat deployen (~15min)
  6. Ein zentrales Git bare repository erstellen (~3min)
  7. In Eclipse unser Maven Projekt unter Git Versionskontrolle stellen (~5min)

1. Apache Archiva konfigurieren

Rufen Sie Apache Archiva im Browser auf und loggen Sie sich mit Ihrem Administrator Benutzer ein, z.B.

Klicken Sie auf Repositories und wenn Sie parallel Eclipse geöffnet haben, sollten Sie in der Eclipse View "Maven Repositories" unter Global Repositories das eingerichtete Archiva als "internal-blogger" sehen, wenn Sie den vorherigen Artikel durchgearbeitet habe:
Sie sehen, dass aktuell 2 remote repositories konfiguriert sind. Diese sind in Apache Archiva bereits vorkonfiguriert gewesen. Wir benötigen nun ein weiteres remote repository, worüber wir das Magnolia CMS beziehen können.
Klicken Sie dazu auf "Add" und füllen Sie die Felder mit folgenden Werden aus:
  • URL: http://nexus.magnolia-cms.com/content/repositories/magnolia.public.releases/
  • Sonstige Feldwerte sehen Sie im Screenshot
Speichern Sie das Repository mit "Add Repository":
Jetzt fehlt noch die Proxy Konfiguration damit das neue Repository auch verwendet werden kann. Klicken Sie dazu auf "Proxy Connectors" und anschließend direkt auf "Add":

Sie haben nun das Magnolia Repository hinzugefügt und können nun in einem Maven Projekt Magnolia dependencies benutzen, die von diesem Repository heruntergeladen werden.

2. Maven multi-module Projekt erstellen

Starten Sie Eclipse und klicken Sie auf "File" -> "New" -> "Other":
Achten Sie darauf, dass Sie als Packaging Typ "pom" ausgewählt haben. Dies macht ein Maven Projekt zu einem Basis Projekt einer Maven multi-module Projektstruktur.


3. Maven submodule als Projekt erstellen

Ein parent Projekt hat die Aufgabe mehrere Unterprojekte zu verwalten, d.h. man kann Maven Goals auf das parent Projekt ausführen und es wird auch auf alle Unterprojekte ausgeführt.

Um ein submodule zu erstellen sind nur 3 Dinge notwendig:
  1. Ein Unterverzeichnis im parent Projekt als Modulverzeichnis
  2. Ein <module> Eintrag in der parent pom.xml
  3. Eine eigene pom.xml im neuen Unterprojekt/Modulverzeichnis
In folgendem Screenshot sehen Sie das Ergebnis nach 1. und 2.
Jedes Maven Projekt hat eine eigene pom.xml Datei. Für das neue Unterprojekt wollen wir Magnolia als Webapp Projekt einrichten. Dazu starten wir mit folgender pom.xml (3.):
Wir können nun das Untermodul als separates Eclipse Projekt importieren. Dies ändert nichts an der Verzeichnisstruktur, lediglich in Eclipse wird sich die Anzeige anpassen.
Klicken Sie auf "File" -> "Import...":
Anschließend wählen Sie das Maven submodule aus. Der Eclipse Wizard wird es erkennen, da im Modulverzeichnis eine pom.xml existiert:

Nun sehen Sie zwei Projekte in Eclipse, das Maven submodule ist als eigenständiges Projekt importiert worden. Das Dateisystem hat sich aber nicht verändert.

Als nächstes können Sie einfach mal das Projekt bauen und zwar mit dem Maven Goal "install". Gehen Sie dazu per Rechtsklick auf das Projekt auf Run As und anschließend auf Maven install:
Es wird ein WAR Archiv im target Verzeichnis erzeugt. Dies können Sie nun auf einem Tomcat Server deployen.
Für alle die an dieser Stelle keinen Tomcat Server zur Verfügung haben, wir werden direkt im Anschluss einen lokalen Tomcat installieren und in Eclipse integrieren, sodass Tomcat direkt aus Eclipse heraus gestartet und gestoppt werden kann.

4. Apache Tomcat in Eclipse integrieren

Wir benötigen zur Integration in Eclipse die Web Tools Platform (WTP), die wir über Help -> Install New Software installieren können:
Nach der erfolgreichen Installation hat man die Servers View in Eclipse zur Verfügung.

Laden Sie nun ein Tomcat Binary zip Paket herunter und entpacken Sie es in ein Verzeichnis Ihrer Wahl. Das Ergebnis sollte ungefähr so aussehen:
Jetzt ist es an der Zeit die Eclipse Servers View anzuzeigen. Gehen Sie zu Window -> Show View -> Other und wählen Sie Servers aus:
Anschließend sehen Sie das Servers Window:
Klicken Sie in diesem Fenster auf den Link "new server wizard" und wählen Sie unter Apache den Tomcat Eintrag mit der passenden Version aus:
Klicken Sie auf Next und wählen Sie Ihr Tomcat Verzeichnis aus:
Im nächsten Schritt bietet Eclipse Ihnen an sofort ein Projekt dem Server hinzuzufügen. Falls Sie bis hierher das Tutorial durchgearbeitet haben, dann wird hier nichts zur Auswahl stehen:
Wir werden auch später diese Hot-Deployment Möglichkeit nicht nutzen, sondern unser Webapp Projekt über das Maven Cargo Plugin per "Knopfdruck" deployen. Das ist zwar kein hot-deploy, aber ich persönlich finde es auch gar nicht schlecht, wenn man zuerst mehrere Dateien bearbeiten kann bevor das Projekt veröffentlicht (deployt? deployed?) wird.
Nach einem Klick auf Finish sollte unser Tomcat in der Servers View integriert sein:
Starten Sie den Tomcat Server nun per Rechtsklick auf den Server und dann auf Start:
Unser Tomcat Server ist nun gestartet, allerdings wird die Tomcat Administrationsoberfläche noch nicht aufrufbar sein:
Stoppen Sie den Server und machen dann einen Doppelklick auf den Tomcat Server Eintrag. Eclipse öffnet anschließend die Konfigurationsübersicht. Wählen Sie unter Server Locations den Radio Button "Use Tomcat installation" aus:
Danach nutzt unser Tomcat das webapps Verzeichnis im lokalen Dateisystem:
Hier landen alle veröffentlichten (deployten?) Webapp Projekte. Bevor wir aber den Server erneut starten müssen wir noch einen Benutzer anlegen. Öffnen Sie mit einem Texteditor folgende tomcat-users.xml Datei:
Konfigurieren Sie einen Benutzer mit Passwort und den folgenden Rollen:
<role rolename="manager-gui"/>
<role rolename="manager-script"/>
<user password="tomcat" roles="manager-gui,manager-script" username="tomcat"/>


Speichern Sie die Datei und starten den Tomcat Server erneut.

Rufen Sie nochmal die Tomcat URL im Browser auf, diesmal sollte ein HTTP Basic Auth Fenster öffnen. Geben Sie Ihre gerade angelegten Benutzerdaten ein:
Anschließend sollte nun die Tomcat Administrationsoberfläche angezeigt werden:
Wir nutzen nun die Gelegenheit, um unser WAR Projekt, welches wir am Ende von Schritt 3 erzeugt haben, auf diesem Tomcat Server zu veröffentlichen. Dieser manuelle Weg dient in erster Linie nur zum Testen von WAR Dateien, während der Entwicklung ist das natürlich ein zu aufwändiger Weg.

Klicken Sie unter "WAR file to deploy" auf den Button "Datei auswählen". Im anschließend geöffneten Dialog wählen Sie die WAR Datei aus dem Projektverzeichnis (rechts):
Klicken Sie im Anschluss auf den Button "Deploy". Sie sollten dann die Webapp in der Liste "Applications" sehen:
Die Webapp sollte gestartet sein und wenn Sie auf den Path Link klicken, wird sich in unserem Fall die Magnolia Installation öffnen, da es der erste Aufruf ist:
Sie sollten nun auch im Tomcat webapps Verzeichnis diese Applikation sehen:
Nach diesem ersten Teilerfolg, werden wir jetzt Maven nutzen, um das Projekt auf unserem Tomcat zu veröffentlichen.

5. Webapp mit Maven auf Server veröffentlichen

Wir benötigen das Maven Cargo Plugin, dazu müssen wir lediglich die pom.xml Datei unseres Webapp Projekts erweitern:
Hier die pom.xml als Text zwecks Copy&Paste:
 
<?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>de.blogspot.sw-technik</groupId>
 <artifactId>myMagnoliaCmsProject</artifactId>
 <version>4.5.7</version>
 <packaging>war</packaging>

 <properties>
  <magnolia.version>${project.version}</magnolia.version>
  <cargo.version>1.2.4</cargo.version>
 </properties>
 
 <build>
  <plugins>
   <plugin>
    <groupId>org.codehaus.cargo</groupId>
    <artifactId>cargo-maven2-plugin</artifactId>
    <version>${cargo.version}</version>
    <!-- Cargo Tomcat --> 
    <configuration>
     <container>
      <containerId>tomcat7x</containerId>
      <type>remote</type>
     </container>
     <configuration>
      <type>runtime</type>
      <properties>
       <!-- /text requires the tomcat-users.xml user/role config with manager-script -->
       <cargo.tomcat.manager.url>http://localhost:8080/manager/text</cargo.tomcat.manager.url>
       <cargo.remote.username>tomcat</cargo.remote.username>
       <cargo.remote.password>tomcat</cargo.remote.password>
      </properties>
     </configuration>
    </configuration>
    <dependencies>
     <dependency>
        <groupId>org.codehaus.cargo</groupId>
        <artifactId>cargo-core-container-tomcat</artifactId>
        <version>${cargo.version}</version>
     </dependency>
    </dependencies>
   </plugin>
  </plugins>
 </build>
 
 <dependencies>
  <dependency>
   <groupId>info.magnolia</groupId>
   <artifactId>magnolia-empty-webapp</artifactId>
   <version>${magnolia.version}</version>
   <type>war</type>
  </dependency>
 </dependencies>
</project>


Jetzt benötigen wir nur noch eine Run Konfiguration, um auch wirklich per "Knopfdruck" das Projekt veröffentlichen zu können.
Gehen Sie im Hauptmenü von Eclipse auf Run -> Run Configurations... und erstellen Sie eine neue Maven Build Konfiguration:
Klicken Sie nun auf Run und sehen Sie in der Console View, wie die Webapp gebaut und deployed wird.

6. Git bare repository erstellen

Ein Git bare repository ist ein spezielles Repository, welches üblicherweise auf einem zentralen Server eingerichtet wird. Ein Git bare repository kann man nicht als Working Copy nutzen, also nicht als Entwicklungsprojekt einrichten. Der Sinn eines bare repository ist, dass man ein zentrales Repository für alle Entwickler hat. Jeder Entwickler klont dieses bare repository und pusht die Änderungen wieder zurück. Somit hält das bare repository den aktuellen Projektstand und kann für Deployments verwendet werden.

In zentralen Versionskontrollsystemen wie Subversion ist der Workflow sehr ähnlich. Der Vorteil von verteilten Versionskontrollsystemen wie Git oder Mercurial ist, dass so ein zentrales Repository optional und sehr leicht einzurichten ist. Und das machen wir jetzt!

Melden Sie sich per SSH auf Ihrem Linux Server an und öffnen Sie die Kommandozeile. Installieren Sie erstmal Git:
apt-get install git

Nach erfolgreicher Installation geben Sie folgende Befehle ein:
mkdir /var/www/parentproject
cd /var/www/parentproject
git --bare init

Prüfen Sie, ob das Repository erstellt wurde. Es sollte folgende Struktur haben:
root@debianVM:/var/www/repository/parentproject# ls
branches  config  description  HEAD  hooks  info  objects  refs

Als Vorbereitung für die Nutzung von Gitweb müssen wir noch einen URL Alias in den VHost von Apache eintragen:
Alias /parentproject "/var/www/repository/parentproject"

Anschließend einfach noch folgende Befehle auf der Konsole nacheinander ausführen:
git update-server-info
cd hooks
mv post-update.sample post-update
/etc/init.d/apache2 restart

Somit ist auf der Server-Side alles für ein zentrales Git bare repository eingerichtet. Jetzt werden wir dieses parentproject Repositoryper Eclipse EGit klonen, um damit arbeiten zu können.

Klicken Sie in der Git Repositories View auf den Link "Clone a Git repository":
Füllen Sie die Felder mit den passenden Daten aus:
Sie sehen, dass das Repository noch keinen master Branch hat und dementsprechend auch noch keinen HEAD hat. Dies werden wir nun ändern.

7. Eclipse Projekt unter Git Versionskontrolle stellen

Wir fügen nun das Eclipse parentproject dem Git Repository parentproject hinzu und damit unter Versionskontrolle.
Gehen Sie folgendermaßen vor: Rechtsklick auf das Projekt -> Team -> Share Project...
Nach einem Klick auf Finish sollte das parentproject mit gelben Säulen markiert sein:
Nun machen wir unseren ersten Git commit. Klicken Sie wieder per Rechtsklick auf das Projekt -> Team -> Commit:
Sobald der Commit durchgeführt wurde können wir die Änderungen auch pushen, d.h. in unser zentrales Git bare repository einspielen.
Dazu gehen wir wie folgt vor: Rechtsklick Projekt -> Team -> Push to Upstream
Jetzt sehen wir auch, dass wir den master Branch "ausgecheckt" (checkout) haben. Das Häkchen markiert den aktuell ausgecheckten Branch.

Wir sollten nun der besseren Entwicklung wegen auch das Webapp Projekt unter Versionskontrolle stellen. Das machen wie wie zuvor mit dem parentproject:
Diesmal müssen wir keinen Commit mehr durchführen, da die Dateien des Webapp Projekts im parentproject Verzeichnis liegen und dies ist ja bereits versioniert.

Leider haben wir einen kleinen Fehler gemacht...

Das kommt hin und wieder auch mal vor, das sollte Sie nicht beunruhigen. Unser Fehler war, dass das target Verzeichnis unter Versionskontrolle von Git steht. Das target Verzeichnis enthält ausschließlich temporär generierte Dateien und Verzeichnisse und diese werden bei jedem Build geändert.

Als kleine Übung im Umgang mit Git überlasse ich die Behebung dieses Fehlers ihnen. Als Hinweis: Sie müssen das target Verzeichnis aus dem Git Index entfernen und anschließend per .gitignore Datei aus der Versionierung ausschließen.

Zusammenfassung

Herzlichen Glückwunsch an dieser Stelle, wenn Sie das Tutorial erfolgreich durchgearbeitet haben. Sie haben nun eine sehr gute Infrastruktur zur Entwicklung unter produktiven Bedingungen durch die Hilfe einer virtuellen Maschine für die Server-Side Installationen.

Sie haben mit Apache Archiva einen Repository Manager mit dem Sie zentral die notwendigen remote Repositories verwalten können und auch JAR Artefakte direkt hochladen können, z.B. wenn es in keinem öffentlichen remote Repository existiert.

Mit dem Maven Cargo Plugin können Sie nicht nur auf einem lokalen Tomcat (oder auch anderen Servern) Ihr Projekt auf einfache Weise veröffentlichen, sondern auch auf einem beliebigen entfernten Server. Sie müssen dazu lediglich die Server Daten in der pom.xml anpassen und natürlich sicherstellen, dass der Server auch entsprechend konfiguriert ist.

Und zu guter letzt haben wir mit Git sowohl auf Client Seite mit Eclipse und EGit, als auch auf der Server-Side mit einem Git bare repository eine solide Versionskontrolle aufgesetzt.

Diese Infrastruktur hat aus meiner Sicht in dieser Form nur einen einzigen Nachteil. Man hat noch kein Berechtigungssystem für die Git Repositories. Jeder der das Repository klonen kann hat auch alle Rechte auf dem Projekt. Das kann manchmal nicht gewünscht sein.

Weitere Schritte


Git hat von Haus aus keine Zugriffskontrolle. In einem Unternehmen kann es aber notwendig werden bestimmten Entwicklern, z.B. Freelancer, eingeschränkten Zugriff auf bestimmte Projekte zu geben. Für diesen Fall gibt es Gitolite oder noch besser Gerrit. Letzteres ist eigentlich kein reines ACL System für Git, sondern ein komplettes Review System. Allerdings bringt Gerrit ein ACL System von Haus aus mit und lässt sich über eine Web GUI auch recht nutzerfreundlich konfigurieren. Ich würde jedem sofort zu Gerrit statt Gitolite raten, auch wenn man erstmal kein Review System benötigt. Gerrit kann man auch an ein Active Directory anbinden, somit steht einer sauberen Integration in eine Enterprise Infrastruktur nichts mehr im Weg.