HTML5 und Javascript zur Darstellung von Graphen

Eingetragen bei: javascript, js | 0

Im folgenden Beispiel soll ein Graph dargestellt und kontinuierlich aktualisiert werden, dessen Daten asynchron von einer Web-Ressource nachgeladen werden. Hierfür kommt die HTML5/Javascript Library RGraph (http://www.rgraph.net/) zum Einsatz, die im privaten Gebrauch umsonst verwendet werden darf. Es ist lediglich ein Verweis auf die Web-Seite von RGraph notwendig. Auf dieser kann die aktuelle Version der Library in Form eines ZIP-Archives bezogen werden. Aus dem Verzeichnis RGraph/libraries werden für dieses Beispiel die folgenden Javascript-Dateien benötigt:

  • jquery.min.js
  • RGraph.common.core.js
  • RGraph.line.js

Diese werden in die HTML-Seite eingebunden:

<script type=“text/javascript“ src=“js/jquery.min.js“ />
<script type=“text/javascript“ src=“js/RGraph.common.core.js“ />
<script type=“text/javascript“ src=“js/RGraph.line.js“ />

Die Grafik wird im HTML5-Element Canvas integriert:

<canvas id=“myCanvas“ width=“600″ height=“250″>[No canvas support]</canvas>

Am Ende der HTML-Seite wird das nun folgende Javascript eingebunden. Zuerst werden die Graph-Daten mit null initialisiert:

d1=[];

for (var i=0; i<250; ++i) {
d1.push(null);
}

Anschließend wir die Funktion getGraph implementiert. Beim ersten Aufruf wird ein Line-Graph erstellt, spätere Aufrufe geben das bereits erzeugte Objekt zurück. Im Konstruktor wird die Id des Canvas und das Daten Array übergeben. Eine vollständige Liste aller möglichen Eigenschaften inklusive ihrer Default-Werte können in RGraph.line.js eingesehen werden:

function getGraph(id, d1)
{
if (!window.line) {
window.line = new RGraph.Line(id, d1);
window.line.Set(‚chart.xticks‘, 100);
window.line.Set(‚chart.title.xaxis‘, ‚Time >>>‘);
window.line.Set(‚chart.title.yaxis‘, ‚Number of Worker‘);
window.line.Set(‚chart.title.vpos‘, 0.5);
window.line.Set(‚chart.title‘, ‚Assigned Jobs‘);
window.line.Set(‚chart.title.yaxis.pos‘, 0.5);
window.line.Set(‚chart.title.xaxis.pos‘, 0.5);
window.line.Set(‚chart.colors‘, [‚red‘]);
window.line.Set(‚chart.linewidth‘,3);
window.line.Set(‚chart.yaxispos‘, ‚right‘);
window.line.Set(‚chart.ymax‘, 30);
window.line.Set(‚chart.xticks‘, 25);
}

return window.line;
}

Nun wird die drawGraph-Funktion erstellt, die einen asynchronen AJAX-Aufruf auf eine Web-Ressource durchführt. In diesem Beispiel liefert sie einen Number-Wert, der im Graph dargestellt werden soll. Auf das Ergebnis kann in der übergebenen Callback-Funktion zugegriffen werden:

function drawGraph() {
RGraph.AJAX(‚http://server_url‘, myCallback);
}

Die Callback-Funktion setzt zunächst das Canvas-Element zurück, um anschließend den Graphen aus der getGraph-Funktion neu zu zeichnen. Über this.responseText erhält man den Rückgabe-Wert der Web-Ressource, welcher zu einer Number konvertiert und anschließend im Daten-Array hinterlegt wird. Sofern das Daten-Array mehr als die maximale Anzahl von 250 Elementen besitzt, wird das erste Element entfernt, so dass die Größe des Graphen trotz fortlaufender Aktualisierung gleich bleibt. Anschließend wird das neue Daten-Array dem Graphen-Objekt zugewiesen und die drawGraph-Funktion mit einer Verzögerung von 100 ms erneut aufgerufen:

function myCallback() {
RGraph.Clear(document.getElementById(„myCanvas“));

var graph = getGraph(‚myCanvas‘, d1);
graph.Draw();
var data = this.responseText;
d1.push(Number(data));

if (d1.length > 250) {
d1 = RGraph.array_shift(d1);
}

if (document.all && RGraph.isIE8()) {
alert(‚[MSIE] Sorry, Internet Explorer 8 is not fast enough to support animated charts‘);
} else {
graph.original_data[0] = d1;
setTimeout(drawGraph, 100);
}
}

Zum Schluss muss die drawGraph-Funktion einmal Initial ausgeführt werden, um die Graphen-Darstellung zu starten:

drawGraph();

Das Resultat kann dann – abhängig von den Daten der Web-Ressource – wie folgt aussehen:chart_example

RGraph wird von allen gängigen Browsern in den jeweils aktuellen Versionen unterstützt. Auch der Internet Explorer akzeptiert ab Version 9 das HTML5-Tag Canvas.

Datei Upload mit Spring 3

Eingetragen bei: Java, spring | 0

Mein letzter Artikel beschäftigte sich mit der Erstellung eines Spring Web MVC Projektes ( http://martinzimmermann1979.wordpress.com/2012/05/23/in-10-minuten-ein-spring-web-mvc-projekt-erstellen/ ). Hier stand ein Controller zur Behandlung von GET- und POST-Requests zur Verfügung. Ersterer lieferte eine Benutzerliste, die in einer entsprechenden View angezeigt wurde. Zweiterer war für das Hinzufügen von weiteren Benutzern zuständig. Die bereits bestehenden Funktionalitäten sollen nun um einen Datei-Upload erweitert werden.

Zusätzlich zu den bereits verwendeten Spring-Libraries, wird folgende Maven dependency benötigt:

<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>

In einem neu zu erstellenden FileController wird die Methode handleUpload benötigt. Da die Datei per POST-Request hochgeladen wird, erhält das Attibute method des RequestMappings den entsprechenden Wert. In der Methoden-Signatur wird diesmal kein
ModelAttribute verwendet, da kein Mapping zwischen Model und Formular-Daten stattfinden muss. Die Binär-Daten sind direkt im Request enthalten und per RequestParam zugreifbar.
Die Datei wird vom Client als Multipart-Request zum Server gesendet, welche in Spring von der Klasse MultipartFile repräsentiert wird. Über die getInputStream-Methode erhält man Zugriff auf den InputStream, der nun verarbeitet werden kann.

@RequestMapping(value = „/file“, method = RequestMethod.POST)
public String handleUpload(@RequestParam(„file“) MultipartFile file) {
if (file.isEmpty()) {
return „redirect:/uploadFailure“;
}
try {
InputStream inputStream = file.getInputStream();
// do with the InputStream, what you want to do
} catch (IOException e) {
return „redirect:/uploadFailure“;
}
return „redirect:/uploadSuccess“;
}

Die obigen Redirects auf die Ressourcen uploadSuccess und uploadFailure führen natürlich noch ins Leere. Wie diese Umzusetzen sind, kann meinem letzten Artikel entnommen werden.

Um per HTLM-Formular eine Datei hochzuladen muss das form-Attribut enctype auf multipart/form-data gesetzt werden. Außerdem wird über die action auf die handleUpload-Ressource verwiesen. Da der RequestParam MultipartFile im RequestParam mit file benannt wurde, muss auch das input-Tag diesen Namen erhalten. Das Attribut type wird ebenfalls auf file gesetzt, damit sich im Browser der Dateiauswahl-Dialog öffnet.

<form action=“file“ method=“post“ enctype=“multipart/form-data“>
<p>Datei:<br>
<input name=“file“ type=“file“>
<input type=“submit“ value=“Absenden“>
</p>
</form>

Die Spring Konfiguration, die für das Hochladen von Dateien notwendig ist, ist recht übersichtlich. Es muss lediglich ein CommonsMultipartResolver definiert werden, damit Spring mit dem MultipartFile umgehen kann:

<bean id=“multipartResolver“
class=“org.springframework.web.multipart.commons.CommonsMultipartResolver“ />

Anschließend können Dateien über das oben definierte Formular auf den Server hochgeladen und dort verarbeitet werden.

In 10 Minuten ein Spring Web MVC Projekt erstellen

Eingetragen bei: Java, spring | 0

Bereits einen vorangegangenen Artikel widmete ich dem Thema REST Services mit Spring 3 (http://martinzimmermann1979.wordpress.com/2012/02/29/rest-services-mit-spring-3) und auch der folgende Beitrag beschäftigt sich mit Spring Web MVC. Diesmal wird allerdings eine Web-Seite realisiert und als Views JSPs (Java Server Pages) verwendet.

Innerhalb von maximal 10 Minuten soll ein vollständiges Spring Web MVC Projekt erstellt werden, mit dem sich eine Benutzerliste darstellen und über ein Formular weitere Benutzer hinzufügen lassen. Zum Einsatz kommt die aktuelle Spring Version 3.1.0. Da ich als Entwicklungsumgebung Eclipse verwende, werden zusätzlich ein paar Eclipse spezifische Details erläutert, grundsätzlich kann das Beispiel aber aus jeder IDE heraus nachvollzogen werden.

Zunächst wird in Eclipse ein Maven Projekt erstellt. Sofern das Eclipse WTP-Plugin (Web Tool Platform – http://www.eclipse.org/webtools/) installiert ist, kann man anschließend über die Projekt Einstellungen, die Project Facets konfigurieren. Hier wird nun das Häkchen bei Dynamic Web Module gesetzt.

Natürlich kann man auch andersherum vorgehen und zuerst ein Web Projekt erstellen und es anschließend zu einem Maven Projekt konvertieren.

Nun werden die drei Spring Libraries Core, Spring-Web und Spring-WebMVC als Maven Dependencys in die pom.xml eingetragen:

<properties>
<spring.version>3.1.0.RELEASE</spring.version>
</properties>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>

Um mit Maven ein Web-Archive (WAR-Datei) erstellen zu können, muss das maven-war-plugin in die Maven-Konfiguration eingetragen und das Packaging von jar auf war umgestellt werden:

<packaging>war</packaging>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.2</version>
</plugin>
</plugins>
</build>

Sobald das Maven-Packaging geändert wird, erscheint in der Problem View von Eclipse folgender Fehler:

Project configuration is not up-to-date with pom.xml. Run Maven->Update Project Configuration or use Quick Fix.

Wie in der Meldung beschrieben, muss lediglich die Projekt-Konfiguration aktualisiert werden, um das Problem zu beheben.

Wer bereits mit WTP gearbeitet hat, ist gewohnt, dass das Web-Verzeichnis unter WebContent zu finden ist. Beim Aktivieren des Facets Dynamic Web Module wurde dieses Verzeichnis irreführender weise angelegt. Da unser Beispiel-Projekt allerdings mit Maven gebaut wird, ist dessen Standardverhalten entscheidend. Daher können die Verzeichnisse WebContent/WEB-INF getrost gelöscht und stattdessen src/main/webapp/WEB-INF angelegt werden. Hier wird vom maven-war-plugin die web.xml Datei erwartet – ist diese nicht vorhanden bricht der Build-Prozess mit einer Fehlermeldung ab.

In der web.xml Datei wird nun das DispatcherServlet definiert, welches alle eingehenden Requests annimmt und an die verantwortlichen Controller delegiert. Das DispatcherServlet ist Bestandteil von Springs IoC-Container und ermöglicht die Nutzung aller von Spring zur Verfügung gestellten Features:

<web-app>
<servlet>
<servlet-name>myWebApp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>myWebApp</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

Der Spring-Dispatcher scannt die unter Spring-Context stehenden Klassen nach Controller Annotationen und erkennt für enthaltene Methoden, ob ein RequestMapping angegeben wurde. Unter dem hier angegebenen Pfad ist die Ressource dann später erreichbar, da das DispatcherServlet entsprechende Requests an die Controller-Methoden delegiert.

Wenden wir uns nun dem UserController zu, der im Package de.mz.server erstellt und mit @Controller annotiert wird. Für die Darstellung der Benutzerliste ist die Methode getUsers verantwortlich, die ein RequestMapping unter dem Pfad users erhält. Da in der web.xml als url-pattern der Root-Pfad angegeben wurde, ist die Ressource unter http://<ip-addresse>:<port>/de.mz.server/users erreichbar. Als Rückgabewert dient der Methode getUsers ein ModelAndView-Objekt. Wie der Name schon sagt, beinhaltet das Objekt die anzuzeigende View und als Model die Benutzerliste:

@Controller
public class UserController {

@RequestMapping(„/users“)
public ModelAndView getUsers() {
List<User> users = loadUsers();
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName(„users“);
modelAndView.addObject(„users“, users);
return modelAndView;
}

// add loadUsers-method
}

Widmen wir uns nun der Spring-Konfiguration, die im WEB-INF-Verzeichnis platziert wird. Der Name der Datei beginnt mit dem in der web.xml definierten Servlet-Namen ergänzt um „-servlet.xml“ – also myWebApp-servlet.xml. Damit der UserController von Spring generiert wird, wird das Component-Scanning für das Package de.mz.server aktiviert:

<context:component-scan base-package=“de.mz.server“/>

Im Controller wurde dem ModelAndView-Objekt der View Name users gesetzt. Um den Namen auf eine View zu mappen, muss ein ViewResolver definiert werden. Abhängig von der View stellt Spring hier verschiedene Strategien zur Verfügung. In diesem Beispiel wird der UrlBasedViewResolver verwendet, der ohne explizites Mapping auskommt, da die Namen der Views automatisch auf die View-Ressourcen gemappt werden:

<bean id=“viewResolver“ class=“org.springframework.web.servlet.view.UrlBasedViewResolver“>
<property name=“viewClass“ value=“org.springframework.web.servlet.view.JstlView“/>
<property name=“prefix“ value=“/WEB-INF/jsp/“/>
<property name=“suffix“ value=“.jsp“/>
</bean>

Als viewClass wird die JstlView gewählt, es werden also JSPs unterstützt, welche die JSP Standard Tag Library verwenden können. Per Präfix wird definiert, dass sich die JSP-Seiten in dem Unterverzeichnis jsp befinden und das Suffix gibt an, dass Views die gleichnamige Datei-Endung besitzen. Die im UserController referenzierte View users befindet sich also unter WEB-INF/jsp/users.jsp und hat folgenden Inhalt:

<%@taglib uri=“http://java.sun.com/jsp/jstl/core“ prefix=“c“%>
<html>
<head><title>Users</title></head>
<body>

<h3>Users</h3>
<table>
<tr>
<th>Vorname</th>
<th>Nachname</th>
</tr>

<c:forEach var=“user“ items=“${users}“>
<tr>
<td>${user.forename}</td>
<td>${user.lastname}</td>
</c:forEach>
</table>

</body>
</html>

Ohne die explizite Angabe der Tag-Library in der ersten Zeile, können die c-Tags nicht verwendet werden. Mit Hilfe des forEach-Befehls wird über die Liste der Benutzer iterieren. Dem items-Attribut wird die Liste der Benutzer zugewiesen, indem auf das im ModelAndView-Objekt hinterlegte Model verwiesen wird. Im var-Feld wird eine temporäre Variable definiert, über die während der Schleifendurchläufe auf die Benutzer zugegriffen werden kann. Vor- und Nachnamen der Benutzer können so in separate Spalten der Tabelle geschrieben werden. Natürlich müssen hierfür die Eigenschaften forename und lastname im Model vorhanden und mittels getter-Methoden zugreifbar sein:

public class User {

private String forename;
private String lastname;

// getter and setter

}

Sofern die Web-Applikation in einem Tomcat-Server laufen soll, muss die JSTL-Library als Maven Dependency eingebunden werden, da diese nicht im Tomcat integriert ist:

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

Im Anschluss kann die Web-Applikation mit Maven gebaut werden. Dies geschieht über den Kommandozeilen-Aufruf mvn package, oder aus der Eclipse IDE heraus. Egal welchen Weg man wählt, nach erfolgreichem Build-Prozess befindet sich die frisch erzeugte WAR-Datei im Target-Verzeichnis und kann deployed werden.

Im zweiten Schritt sollen weitere Benutzer über ein Formular hinzufügbar sein. Hierzu wird der UserController um die Methode addUser erweitert. Über das RequestMapping wird der Pfad auf user und POST als Request-Art gesetzt. Als Übergabe-Parameter kann direkt das Model – also die User-Klasse – verwendet werden, da sich Request-Parameter bequem mit Objekt-Eigenschaften verknüpfen lassen. Hierzu reicht es aus, den Übergabe-Parameter mit der Annotation ModelAttribute zu versehen. Der hier vergebene Name userForm muss später auch im HTML-Formular verwendet werden. Nach erfolgreichem Speichern des Benutzers, soll anschließend ein redirect auf die users-Ressource stattfinden, damit die vervollständigte Benutzerliste betrachtet werden kann. Daher ist der Rückgabe-Wert diesmal ein String und kein ModelAndView-Objekt:

@RequestMapping(value=“/user“, method = RequestMethod.POST)
public String addUser(@ModelAttribute(„userForm“) User user) {
// save user
return „redirect:/users“;
}

Widmen wir uns dem HTML-Formular. Die Input-Tags im HTML-Formular müssen die gleichen Namen verwenden, wie die entsprechenden Eigenschaften in der User-Klasse und es müssen die dazugehörigen setter-Methoden existieren. Das Form-Attribut modelAttribute erhält denselben Namen, wie die gleichnamige Annotation im Controller, um das Mapping zwischen Input-Feldern und Eigenschaften der User-Klasse zu ermöglichen. Als action wird der Pfad zur addUser-Methode und als method POST angegeben, was den RequestMapping-Einstellungen des Controllers entspricht:

<form action=“user“ modelAttribute=“userForm“ method=“POST“>
<p>Vorname:<br><input name=“forename“ type=“text“></p>
<p>Nachname:<br><input name=“lastname“ type=“text“></p>
<p><input type=“submit“ value=“Absenden“></p>
</form>

Nach einem Redeployment können wie in der Zielsetzung beschrieben, nun weitere Benutzer hinzugefügt werden.

Innerhalb kürzester Zeit wurde mit Spring 3.x eine Web-Applikation erstellt, die aus Controller und View zur Administrierung von Benutzern besteht. Hierfür wurde das DispatcherServlet in die web.xml-Datei eingetragen und für die Auswahl der richtigen View ein ViewResolver verwendet, der den View-Namen auf eine JSP-Datei mappt. Alle weiteren Einstellungen ließen sich bequem per Annotation direkt im Controller vornehmen.

Eclipse Libra

Eingetragen bei: Eclipse, Java, RCP | 0

Bei Eclipse Libra handelt es sich um ein Unter-Projekt vom Web-Tools-Platform-Projekt (WTP), welches den Funktionsumfang von WTP und dem Plug-in Development Environment (PDE) vereint. So können in einem WTP-Projekt auch die Werkzeuge eines PDE-Projektes genutzt werden.

Für die Installation von Libra unter der aktuellen Eclipse Version Indigo reicht es, auf der Indigo Update-Site die Bundles OSGI Bundle Facet und WAR Products zu installieren.

Erstellt man ein Dynamic Web Project können auf der ersten Seite des Wizards unter Configuration die Project Facets konfiguriert werden. Nach der Installation der oben genannten Pakete ist hier der Menüpunkt OSGI Bundle hinzugekommen, den man auswählt. Dies führt dazu, dass im Wizard eine weitere Seite verfügbar ist, auf der Details für das Manifest angegeben werden können. Das Manifest selbst befindet sich im META-INF Verzeichnis des Web-Ordners, also eine Hierarchiestufe tiefer als in herkömmlichen OSGI Bundles.

Um das Web-Projekt testen zu können, ist zumindest ein Servlet nötig, welches in der web.xml definiert wird:

  <servlet>
<servlet-name>hello</servlet-name>
<servlet-class>test.TestServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>

Das Servlet selber erbt von der Klasse javax.servlet.http.HttpServlet und überschreibt die doGet-Methode.

Sofern nicht bereits eine passende Target-Platform definiert wurde, wird es spätestens bei der Implementierung des Servlet zu einem Compile-Fehler kommen, da javax.servlet nicht aufgelöst werden kann. Zur Ausführung des Web-Projektes mit dem OSGI-Framework kann die Target-Platform von http://wiki.eclipse.org/Gemini/Web verwendet werden.

Bei Gemini selbst, handelt es sich um modulare Enterprise-Technologien, welche speziell für das OSGI-Framework konzipiert wurden.

Für die Installation werden folgende Archive benötigt: http://www.eclipse.org/downloads/download.php?file=/equinox/drops/R-3.7.1-201109091335/org.eclipse.osgi_3.7.1.R37x_v20110808-1106.jar und http://www.eclipse.org/downloads/download.php?file=/gemini.web/release/GW/2.0.1.RELEASE/gemini-web-2.0.1.RELEASE.zip.

Anschließend wird gemini-web in ein beliebiges Verzeichnis entpackt und org.eclipse.osgi.jar in eben dieses kopiert. Nun wird hier noch das Verzeichnis configuration erstellt und die folgenden beiden Konfigurationsdateien hinein kopiert:

http://wiki.eclipse.org/images/0/0b/Config.ini.2.0.1.zip

http://wiki.eclipse.org/images/3/32/Java6-server.profile.zip

Jetzt wird in Eclipse eine neue Target-Platform (Window / Preferences / Target Platform) erstellt, deren Location auf das gemini-web Verzeichnis verweist.

Als nächstes wird eine neue Run-Configuration unter dem Punkt OSGI Framework erstellt. Unter den VM-Arguments wird folgender Parameter hinzugefügt:

-Dosgi.java.profile=file:configuration/java6-server.profile

Außerdem muss als Working directory das gemini-web Verzeichnis angegeben werden.

Weitere Schritte sind nicht nötig, nun kann die Run-Configuration ausgeführt werden und die Web Applikation ist unter http:127.0.0.1:8080/<application-name>/hello verfügbar. Der Applikations-Name wird hier standardmäßig genommen, will man dies ändern, so ist der Parameter Web-ContextPath im Manifest anzupassen.

Die Facets von Eclipse Libra lassen sich nicht nur in WTP-Projekten nutzen. Es ist durchaus möglich, dieses Facet z.B. einem Maven-Projekt zuzuweisen, um hier die PDE Funktionalitäten nutzen zu können. Der Nachteil zu Apache-Felix ist hierbei allerdings, dass die pom.xml und das Manifest separat gepflegt werden müssen. Werden also Maven-Dependendies hinzugefügt, müssen diese auch im Manifest eingetragen werden, damit die Abhängigkeiten zur Laufzeit verwendbar sind.