Videoautomat Web

Aus Salespoint

(Unterschied zwischen Versionen)
Wechseln zu: Navigation, Suche
(comment5, http://lignvenicar.szm.com/page_462.html îðãàíèçàöèÿ êàê ôóíêöèÿ ìåíåäæìåíòà äàéòå ñêà÷àòü, =[[, http://lisynleguar.chez.com/page_441.html ñêà÷àòü áåñïëàòíî àëüáîìû ãðó)
(comment5, http://lbizkinrekil.chez.com/site-197.html ãóô è àéçà-ïðèçíàíèå ñêà÷àòü áåñïëàòíî, rrencl, http://libtastvenli.szm.com/doc_107.html ðåôåðàò áèîëîãèÿ ïðèíöèïû êëàññèôèêàöè)
Zeile 3: Zeile 3:
comment5, http://lignvenicar.szm.com/page_462.html îðãàíèçàöèÿ êàê ôóíêöèÿ ìåíåäæìåíòà äàéòå ñêà÷àòü,  =[[, http://lisynleguar.chez.com/page_441.html ñêà÷àòü áåñïëàòíî àëüáîìû ãðóïïû êàáðèîëåò,  trtymc, http://keyposihan.chez.com/site-279.html ñêà÷àòü îïåðó íà êîìïüþòåð ÷åðåç òåëåôîí,  wtb, http://knoljesmidis.szm.com/doc_432.html ïðîãðàììà ïî ïîäáîðó íàñîñà ñêà÷àòü,  92248, http://lignvenicar.szm.com/page_464.html ýðîòè÷åñêèå ìóëüòèêè áåëîñíåæêà ñêà÷àòü,  =-[[[, http://lignvenicar.szm.com/page_232.html ñêà÷àòü ïåñíè âåðû êóøíèð,  2050,
comment5, http://lignvenicar.szm.com/page_462.html îðãàíèçàöèÿ êàê ôóíêöèÿ ìåíåäæìåíòà äàéòå ñêà÷àòü,  =[[, http://lisynleguar.chez.com/page_441.html ñêà÷àòü áåñïëàòíî àëüáîìû ãðóïïû êàáðèîëåò,  trtymc, http://keyposihan.chez.com/site-279.html ñêà÷àòü îïåðó íà êîìïüþòåð ÷åðåç òåëåôîí,  wtb, http://knoljesmidis.szm.com/doc_432.html ïðîãðàììà ïî ïîäáîðó íàñîñà ñêà÷àòü,  92248, http://lignvenicar.szm.com/page_464.html ýðîòè÷åñêèå ìóëüòèêè áåëîñíåæêà ñêà÷àòü,  =-[[[, http://lignvenicar.szm.com/page_232.html ñêà÷àòü ïåñíè âåðû êóøíèð,  2050,
-
=Spring MVC Roundtrip=
+
comment5, http://lbizkinrekil.chez.com/site-197.html ãóô è àéçà-ïðèçíàíèå ñêà÷àòü áåñïëàòíî, rrencl, http://libtastvenli.szm.com/doc_107.html ðåôåðàò áèîëîãèÿ ïðèíöèïû êëàññèôèêàöèè âèäîâ ñêà÷àòü, qyaljp, http://lignvenicar.szm.com/page-398.html toxic áðèòíè ñêà÷àòü avifrqvb, http://lisynleguar.chez.com/page-92.html ïóøêè îñòðîâà íàâàðîí ôèëüì ñêà÷àòücdwdex, http://knowantota.szm.com/site-115.html ïåñíÿ è ñàðóõàíîâà ñêà÷àòü039906, http://lechanrochess.chez.com/skachat-besplatno-inek-analitik.html ñêà÷àòü áåñïëàòíî èíýê-àíàëèòèê, 015, http://lobswelersver.chez.com/skachat-player-baby-come-back.html ñêà÷àòü player baby come back,  026818,
-
==Startseite mit Login==
+
-
Nun haben wir das Model aus dem MVC-Pattern aufgebaut und können unseren ersten Controller mit entsprechender View erstellen. Zunächst soll die Startseite der Applikation erstellt werden, auf der man sich einloggen sowie nach dem Login den Videokatalog ansehen kann.
+
-
 
+
-
Dazu erstellen wir die Datei ''WebContent/jsp/index.jsp'' mit folgendem Inhalt:
+
-
<code xml>
+
-
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+
-
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+
-
<%@ taglib uri="http://www.salespoint-framework.org/web/taglib" prefix="sp" %>
+
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+
-
<html xmlns="http://www.w3.org/1999/xhtml">
+
-
  <head>
+
-
    <meta http-equiv="Content-Type" content="text/html; charset=uft-8" />
+
-
    <link rel="stylesheet" type="text/css" href="<c:url value="/static/resources/css/style.css" />" />
+
-
    <title>SalesPoint2010-Videoautomat</title>
+
-
  </head>
+
-
  <body>
+
-
    <sp:Messages messages="${spErrors}" />
+
-
    <sp:Messages messages="${spFlash}" />
+
-
 
+
-
    <sp:LoggedIn status="false">
+
-
      <sp:LoginDialog />
+
-
    </sp:LoggedIn>
+
-
 
+
-
    <sp:LoggedIn status="true">
+
-
      <sp:List abstractTableModel="${videoCatalog}" />
+
-
    </sp:LoggedIn>
+
-
 
+
-
  </body>
+
-
</html>
+
-
</code>
+
-
In der JSP sehen wir neben gewöhnlichen HTML-Tags im oberen Bereich taglib-Deklarationen und im unteren Bereich dann dessen Verwendung. TagLibraries sind vorgefertigte Konstrukte, die zur Auswertung mit echtem HTML ersetzt werden. Salespoint stellt hiervon einige sehr nützliche bereit. Diese werden unter [[Web Erweiterung#TagLibrary]] detailiert beschrieben. Für uns im Moment wichtig zu wissen ist, dass der Message-Tag jeweils eine Liste von Strings HTML-formatiert ausgibt (Fehler bzw. Erfolgsnachrichten). Der LoggedIn-Tag rendert seinen Inhalt immer dann, wenn der diese Seite aufrufende Nutzer entweder eingeloggt ist oder nicht (entsprechend dem Attribut status). Der LoginDialog stellt ein LoginFormular bereit und der List-Tag listet uns ein übergebenes ''AbstractTableModel'' auf, welches eine Abstraktion von Katalogen und Beständen darstellt. Auffällig sind die Konstrukte alá "${videoCatalog}". Das sind Java-Objekte aus der sog. ModelMap, die wir im Controller befüllen.
+
-
 
+
-
Dazu erstellen wir eine Klasse im Package bzw. in einem Unterpaket, welches nach Springkomponenten durchsucht (siehe oben) wird.
+
-
<code java>
+
-
package org.salespointframework.web.examples.videoautomat.controller;
+
-
 
+
-
@Controller
+
-
@Interceptors({LoginInterceptor.class})
+
-
public class Maincontroller {
+
-
  @RequestMapping("/")
+
-
  public ModelAndView index(ModelAndView mav) {
+
-
    ATMBuilder<VideoCatalog> videoCatalog = new ATMBuilder<VideoCatalog>(VideoShop.getVideoCatalog());
+
-
    mav.addObject("videoCatalog",videoCatalog.getATM());
+
-
    mav.setViewName("index");
+
-
    return mav;
+
-
  }
+
-
}
+
-
</code>
+
-
Wir annotieren die Klasse mit ''@Controller'', damit Spring und der DispatchServlet diese Klasse kennen und sie nach Methoden durchsuchen, die mit ''@RequestMapping'' annotiert sind. Somit weiß der DispatchServlet, dass er Anfragen auf das Wurzelverzeichnis unserer Webapplikation auf die auch als Action bezeichnete Methode ''MainController.index()'' weiterzuleiten hat. Diese wiederum erstellt bzw. befüllt das übergebene ModelAndView-Object mit den Daten, die wir in unserer JSP brauchen und gibt mit setViewName() auch die JSP an (der in der ''dispatch-servlet.xml'' angegebene viewResolver löst den String zu einem Pfad auf).
+
-
Der ATMBuilder baut uns dem übergebenen VideoCatalog, den wir uns aus unserem VideoShop-Singleton holen, ein AbstractTabelModel, welches wir in das ModelAndView legen.
+
-
Die zweite Annotation auf Klassenebene ''@Interceptors({LoginInterceptor.class})'' ist notwendet für die Loginfunktionalität. Interceptor in Spring ''unterbrechen'' die Ausführung von Actions, in dem sie entweder vor oder nach der Action ausgeführt werden. Die Annotation sagt aus, dass alle Methoden im MainController vom LoginInterceptor unterbrochen werden.
+
-
 
+
-
Doch genug der grauen Theorie, nun ist der richtige Zeitpunkt, die Webapplikation zum ersten Mal zu starten. Es sollte ein Loginformular zu sehen sein mit dem Administrator (leeres Passwort). Nach dem Klick auf den LoginButton sollte eine Liste mit Videos zu sehen sein.
+
-
 
+
-
==Nutzerregistrierung==
+
-
Damit wir zwischen verschiedenen Nutzern unterscheiden können, brauchen diese eine Repräsentation im Model - einen Nutzeraccount. Hierzu können wir eine einfache Formularverarbeitung von Spring nutzen.
+
-
Zuerst einmal fügen wir auf der Startseite einen Link auf das Registrierformular hinzu:
+
-
<code xml>
+
-
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
+
-
.
+
-
<sp:LoggedIn status="false">
+
-
  <sp:LoginDialog />
+
-
  <a href="register"><spring:message code="videoautomat.register" /></a>
+
-
</sp:LoggedIn>
+
-
.
+
-
</code>
+
-
Augenmerk hierbei liegt auf dem MessageTag aus dem Spring-Namespace. Dieser schaut in unserer MessageSource nach diesem Code und ersetzt ihn mit der richtigen Sprachversion. Wenn man von Beginn einer neuen Webapplikation dieses Mechanismus nutzt, macht es bis auf das Übersetzen der ''messages.properties'' '''keine''' Zusatzarbeit, die gesamte Applikation in anderen Sprachen anzubieten. Wir fügen also diesen Code in unsere StandardMessageSourceDatei ein:
+
-
<code java>
+
-
videoautomat.register = register
+
-
</code>
+
-
Die nächste Aufgabe ist es, eine Action für diese URL zu definieren. Aus Bequemlichkeit gleich im MainController:
+
-
<code java>
+
-
.
+
-
@RequestMapping(value="/register",method=RequestMethod.GET)
+
-
public ModelAndView registerForm(ModelAndView mav) {
+
-
  mav.setViewName("registerForm");
+
-
  return mav;
+
-
}
+
-
.
+
-
</code>
+
-
Diese Action nimmt ausschließlich GET-Anfragen auf der URL "/register" an und bestimmt ausschließlich die View(''WebContent/jsp/registerForm.jsp''):
+
-
<code xml>
+
-
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+
-
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+
-
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
+
-
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
+
-
<%@ taglib uri="http://www.salespoint-framework.org/web/taglib" prefix="sp" %>
+
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+
-
<html xmlns="http://www.w3.org/1999/xhtml">
+
-
  <head>
+
-
    <meta http-equiv="Content-Type" content="text/html; charset=uft-8" />
+
-
    <link rel="stylesheet" type="text/css" href="<c:url value="/static/resources/css/style.css" />" />
+
-
    <title>SalesPoint2010-Videoautomat</title>
+
-
  </head>
+
-
  <body>
+
-
    <sp:Messages messages="${spErrors}" />
+
-
    <sp:Messages messages="${spFlash}" />
+
-
+
-
    <form:form method="post" action="register">
+
-
      <table>
+
-
        <tr>
+
-
          <td><spring:message code="videoautomat.userName" /></td>
+
-
          <td><input type="text" name="userName" /></td>
+
-
        </tr>
+
-
        <tr>
+
-
          <td><spring:message code="videoautomat.userPasswd" /></td>
+
-
          <td><input type="password" name="userPasswd" /></td>
+
-
        </tr>
+
-
        <tr>
+
-
          <td></td>
+
-
          <td><input type="submit" value="<spring:message code="videoautomat.register" />" /></td>
+
-
        </tr>
+
-
      </table>
+
-
    </form:form>
+
-
 
+
-
  </body>
+
-
</html>
+
-
</code>
+
-
sowie die benutzten neuen MessageSourceCodes:
+
-
<code java>
+
-
videoautomat.userName = your name
+
-
videoautomat.userPasswd = password
+
-
</code>
+
-
Das HTML-Formular wird wie üblich als POST-Anfrage abgesendet und zwar an dieselbe URL ("/register"). Wir benötigen also eine weitere Action, die nun die POST-Anfragen mit den mitgesendeten Parametern annimmt. Spring bietet dafür eine sehr einfache Variante an, in dem annotationsbasiert diese Requestparameter an Parameter der Action gebunden werden.  
+
-
<code java>
+
-
@Controller
+
-
@Interceptors({LoginInterceptor.class})
+
-
public class MainController {
+
-
  .
+
-
  .
+
-
  @Autowired
+
-
  private MessageSource messageSource;
+
-
 
+
-
  public void setMessageSource(MessageSource messageSource) {
+
-
    this.messageSource = messageSource;
+
-
  }
+
-
  .
+
-
  .
+
-
  @RequestMapping(value="/register",method=RequestMethod.POST)
+
-
  public ModelAndView register(
+
-
          @RequestParam("userName") String userName,
+
-
          @RequestParam("userPasswd") String userPasswd,
+
-
          HttpServletRequest request,
+
-
          ModelAndView mav) {
+
-
   
+
-
    if(userName.isEmpty())
+
-
      MessagesUtil.addError(mav, messageSource.getMessage("videoautomat.empty", new String[]{"user name"}, RequestContextUtils.getLocale(request)));
+
-
    if(userPasswd.isEmpty())
+
-
      MessagesUtil.addError(mav, messageSource.getMessage("videoautomat.empty", new String[]{"password"}, RequestContextUtils.getLocale(request)));
+
-
    if(UserManager.getInstance().getUser(userName) != null)
+
-
      MessagesUtil.addError(mav, messageSource.getMessage("videoautomat.userNameNotFree", new String[]{userName}, RequestContextUtils.getLocale(request)));
+
-
 
+
-
    if(MessagesUtil.hasErrors(mav)) {
+
-
      return registerForm(mav);
+
-
    } else {
+
-
      AutomatUser usr = new AutomatUser(userName, userPasswd);
+
-
      UserManager.getInstance().addUser(usr);
+
-
      MessagesUtil.addFlash(mav,
+
-
      mav.setViewName("redirect:/");
+
-
      return mav;
+
-
    }
+
-
  }
+
-
}
+
-
.
+
-
</code>
+
-
mit den neuen MessageSourceCodes:
+
-
<code java>
+
-
videoautomat.empty = input field ''{0}'' should not be empty
+
-
videoautomat.userNameNotFree = user name ''{0}'' is already taken by another user
+
-
</code>
+
-
OK, nun der Reihe nach:
+
-
*;MessageSource: Die über dem Attribut definierte Annotation ''@Autowired'' gibt Spring Bescheid, dass eine unter diesem Namen instanziierte JavaKlasse über die entsprechende Setter-Methode in diese Klasse injiziert werden soll (wir erinnern uns an den in der ''dispatch-servlet.xml'' definierten beanTag mit der id="messageSource"). Diese Instanz kann auf diese Weise zwischen allen Klassen der Applikation gemeinschaftlich und entsprechend speicherschonend benutzt werden, um lokalisierte Strings aufzulösen. Um das aktuelle Locale-Object zu bekommen, gibt es einen Toolbox-Klasse von Spring, die es aus dem ''Request''-Object, welches man sich einfach mit übergeben lassen kann, herausholt. Ein Zusatzfeature hierbei ist, dass auch für MessageSourceCodes Parameter mitgegeben und per geschweiften Klammern mit dem Index darin referenziert werden.
+
-
*;MessageUtil: Toolbox zum Hinzufügen(add)/Abfragen(has)/Löschen(clear) von Fehler-(Error) oder Erfolgs-(Flash)nachrichten. Diese werden in einer Liste von Strings unter den Schlüsseln "spErrors" bzw. "spFlash" in der ModelMap abgelegt und können unter diesen auch wieder abgefragt werden (siehe JSP).
+
-
 
+
-
Falls etwas schiefgegangen ist und Fehler dem ModelAndView hinzugefügt wurden, wird diesem der registerForm()-Methode übergeben, die auf der JSP das Formular sowie die Fehlermeldungen aus der MAV anzeigt. Falls keine Fehler aufgetreten sind, wird eine neue Instanz der Klasse AutomatUser erstellt, dem UserManager hingefügt sowie auf die Hauptseite "geroutet".
+
-
 
+
-
Diese Funktionalität sollte nun erstmal wieder getestet werden.
+
-
 
+
-
==Ausleih- und Bezahlvorgang==
+
-
Um Elemente von einem Catalog/Bestand benutzereigabengesteuert in einen anderen zu verschieben sollte stets die DoubleViewController/DoubleViewTag-Kombination verwendet werden. Der DoubleViewController stellt auf Basis seiner Initialisierung sämtliche Verschiebeaktionen bereit.
+
-
 
+
-
Wenn ein Nutzer Videos ausleiht, verschiebt er sie vom Bestand des Automaten in seinen eigenen Videobestand. Diese Verschiebeaktion gilt allerdings solange als temporär, bis er sie im 2.Schritt auch bezahlt hat. Dies ist also ein Einsatzgebiet vom Salespoint ''DataBasket''. Eine Instanz vom DataBasket speichert die Information, welche Videos der Nutzer ausleihen will, und wenn er sie bezahlt hat, wird auf ihm ''commit()'' aufgerufen und damit die Verschiebeaktion persistent oder aber, falls der Nutzer sich anders entschieden hat und die Aktion abbricht, wird mit ''rollback()'' die temporäre Verschiebeaktion einfach rückgängig gemacht.
+
-
 
+
-
===Ausleihen===
+
-
Ein Link von der Hauptseite ''WebContent/jsp/index.jsp'' aus bietet den Zugang zum Ausleihvorgang:
+
-
<code xml>
+
-
.
+
-
<sp:LoggedIn status="true">
+
-
  <sp:List abstractTableModel="${videoCatalog}" />
+
-
  <a href="rent"><spring:message code="videoautomat.rent"/></a>
+
-
</sp:LoggedIn>
+
-
.
+
-
</code>
+
-
Auf die Angabe der entsprechenden MessageSourceCodes wird absofort verzichtet.
+
-
 
+
-
Eine neue Controller-Klasse, die vom DoubleViewController erbt, bildet die Ausgangsbasis. Fundamental wichtig hierbei ist die richtige Typisierung dieser Klasse. Macht euch bewusst, von welchem Datentyp zu welchem verschoben werden soll.
+
-
<code java>
+
-
@Controller
+
-
@Scope("session")
+
-
@RequestMapping("/rent")
+
-
public class RentController extends DoubleViewController<AutomatVideoStock, UserVideoStock> {
+
-
 
+
-
  @RequestMapping("")
+
-
  public ModelAndView index(ModelAndView mav, HttpServletRequest request, HttpServletResponse response) {
+
-
   
+
-
    initialize(
+
-
    /*source*/      VideoShop.getVideoStock(),
+
-
    /*destination*/ ((AutomatUser)UserManager.getInstance().getCurrentUser(request.getSession())).getVideoStock(),
+
-
    /*moveStrategy*/ new CSSSStrategy() {
+
-
                      @Override protected StockItem createStockItem(StockItem ci) {
+
-
                        return new VideoCassette(ci.getName());
+
-
                      }
+
-
                    },
+
-
    /*dataBasket*/  new DataBasketImpl());
+
-
+
-
    mav.setViewName("rent");
+
-
+
-
    mav.addObject("videoStock", new ATMBuilder<AutomatVideoStock>(getSource())
+
-
                                .db(getDataBasket())
+
-
                                .getATM());
+
-
+
-
    mav.addObject("basket", new ATMBuilder<DataBasket>(getDataBasket())
+
-
                                .ted(new VideoCassetteDBESSTED())
+
-
                                .dbc(DataBasketConditionImpl.allStockItemsWithDest(getDestination()))
+
-
                                .getATM());
+
-
 
+
-
    return mav;
+
-
  }
+
-
}
+
-
</code>
+
-
Nun wieder der Reihe nach die Neuerungen:
+
-
*;@Scope("session"): Diese Annotation besagt, dass für jeden Nutzer eine neue Instanz dieses Controllers angelegt wird. Dies ist notwendig, da wir hier einen sog. ''stateful'' Controller habn, der über mehrere Requests hinweg unterschiedliche Daten für einen speziellen Nutzer speichert. Controller ohne explizit angegebenem Scope werden von Spring nur einmal instanziiert und für alle Nutzer verwendet.
+
-
 
+
-
*;@RequestMapping: Die RequestMapping-Annotation kann auch auf Klassenebene verwendet werden und dessen Attribut wird dann als Prefix für alle Actions verwendet. Dies ist beim DoubleViewController sogar notwendet, da die ''index()''-Action '''unbedingt''' ein leeres RequestMapping haben muss.
+
-
 
+
-
*;initialize(): Mit dieser Methode wird der DoubleViewController initialisiert. Dies sollte im Normalfall im Konstruktor des Controllers geschehen oder aber, wie in diesem Fall, in der index()-Action, wenn z.B. der Request nötig ist. Über den Request kommt man an das Session-Object über das ein Nutzer im UserManager angemeldet wird.
+
-
**;source: Der Quellbestand, aus dem heraus verschoben werden soll.
+
-
**;dest: Der Zielbestand, in den verschoben werden soll.
+
-
**;moveStrategy: Die richtige(!) MoveStrategy. In unserm Fall ist der Quellbestand - der AutomatVideoStock - ein ''CountingStock'', der Zielbestand - der UserVideoStock - ein ''StoringStock'' -, also brauchen wir die ''CSSSStrategy'' aus dem Web-Package. Nicht alle dieser Strategien sind konkret, da z.b. aus einem CountingStock kein StockItem wieder herausgeholt werden kann. Deshalb muss in diesem Fall eine FactoryMethod implementiert werden, die ein frisches konkretes VideoCassette-Objekt aus einem StockItemImpl generiert.
+
-
**;db: Eine DataBasket-Instanz, in der die Verschiebeaktionen gespeichert werden, um sie im Anschluss zu ''commit''tn oder zu ''rollback''n.
+
-
*;ATMBuilder: Den ATMBuilder haben wir bereits auf der Startseite kennengelernt. Hier wird er allerdings in einer Version genutzt, die sich ''FluentBuilder'' nennt und auf dem ''MethodChaining''-Prinzip basiert. Methoden, die im Javadoc der Klasse als MethodChaining-Methoden ausgegeben wurden, geben nach dem Setzen des Attributes das Object selbst zurück, sodass in der selben Zeile damit weitergearbeitet werden kann.
+
-
*;VideoCassetteDBESSTED: Mit einem ''TableEntryDescriptor''(TED) kann man bestimmen, wie sich die Elemente eines Catalogs/Stocks/DataBaskets in einer Visualisierung beschreiben sollen. Da wir hier einen DataBasket visualisieren wollen und dafür kein DefaultTED existiert, müssen wir einen angeben, der z.B. so aussieht:
+
-
<code java>
+
-
public class VideoCassetteDBESSTED extends AbstractTableEntryDescriptor {
+
-
 
+
-
  private static final String[] cNames = { "Name", "Price" };
+
-
  private static final Class<?>[] cClasses = { String.class, Double.class };
+
-
 
+
-
  public int getColumnCount() {
+
-
    return cNames.length;
+
-
  }
+
-
 
+
-
  public String getColumnName(int nIdx) {
+
-
    return cNames[nIdx];
+
-
  }
+
-
 
+
-
  public Class<?> getColumnClass(int nIdx) {
+
-
    return cClasses[nIdx];
+
-
  }
+
-
 
+
-
  @Override
+
-
  public Object getValueAt(Object oRecord, int nIdx) {
+
-
    StoringStockItemDBEntry dbe = (StoringStockItemDBEntry)oRecord;
+
-
    VideoCassette si = (VideoCassette)dbe.getValue();
+
-
+
-
    switch (nIdx) {
+
-
      case 0: return si.getName();
+
-
      case 1: return new DecimalFormat("#.## &euro;").format(((NumberValue)si.getAssociatedItem(null).getValue()).getValue());
+
-
    }
+
-
    return null;
+
-
  }
+
-
}
+
-
</code>
+
-
Jetz haben wir 2 ATMs in der ModelMap und überführen diese, wie auf der Startseite, in der neuen ''WebContent/jsp/rent.jsp'' jeweils in einen ViewTag, die sich diesmal jedoch in einem DoubleViewTag befinden.
+
-
<code xml>
+
-
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+
-
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
+
-
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
+
-
<%@ taglib uri="http://www.salespoint-framework.org/web/taglib" prefix="sp" %>
+
-
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+
-
<html xmlns="http://www.w3.org/1999/xhtml">
+
-
  <head>
+
-
    <meta http-equiv="Content-Type" content="text/html; charset=uft-8" />
+
-
    <link rel="stylesheet" type="text/css" href="<c:url value="/static/resources/css/style.css" />" />
+
-
  </head>
+
-
  <body>
+
-
    <sp:Messages messages="${spErrors}" styleName="error"/>
+
-
    <sp:Messages messages="${spFlash}" styleName="flash"/>
+
-
 
+
-
    <sp:LoggedIn status="true">
+
-
      <sp:DoubleView showNumberField="true">
+
-
        <sp:Table abstractTableModel="${videoStock}" />
+
-
        <sp:Table abstractTableModel="${basket}" />
+
-
      </sp:DoubleView>
+
-
 
+
-
      <form:form method="get" action="pay" id="pay">
+
-
        <input type="submit" value="<spring:message code="videoautomat.rent" />" />
+
-
      </form:form>
+
-
      <form:form method="get" action="rent/cancel" id="rent">
+
-
        <input type="submit" value="<spring:message code="videoautomat.cancel" />" />
+
-
      </form:form>
+
-
 
+
-
    </sp:LoggedIn>
+
-
 
+
-
  </body>
+
-
</html>
+
-
</code>
+
-
2 weitere Buttons fügen wir ein und bestimmen action-Attribut, wohin die Reise geht, wenn der entsprechende Submit-Button gedrückt wird. Ersterer verlinkt nach "/pay", was in der nächsten Rubrik behandelt wird. Der zweite Button ruft die URL "rent/cancel" auf. Hierfür definieren wir eine Action im RentController:
+
-
<code java>
+
-
.
+
-
@RequestMapping("/rent")
+
-
public class RentController extends DoubleViewController<AutomatVideoStock, UserVideoStock> {
+
-
.
+
-
  @RequestMapping("/cancel")
+
-
  public ModelAndView cancel(ModelAndView mav, HttpServletRequest request) {
+
-
    getDataBasket().rollback();
+
-
    mav.setViewName("redirect:/");
+
-
    return mav;
+
-
  }
+
-
}
+
-
</code>
+
-
Wie bereits erwähnt werden die RequestMapping-Annotation-Attribute konkateniert und ergeben die richtige URL. Auf dem DataBasket wird ''rollback()'' aufgerufen und damit verfallen alle getätigten Verschiebeaktionen. Danach wird kein echter ViewName gesetzt, sondern ein Redirect ausgelöst, der den ClientBrowser auf die Startseite weiterleitet.
+
-
 
+
-
===Bezahlen===
+
-
Für das Ziel des "pay"-Buttons auf der Ausleihseite - die Bezahlseite - brauchen wir einen neuen Controller, der von DoubleViewController erbt und auf die URL "/pay" lauscht:
+
-
<code java>
+
-
@Controller
+
-
@Scope("session")
+
-
@RequestMapping("/pay")
+
-
public class PayController extends DoubleViewController<Currency<CurrencyItemImpl>, MoneyBag> {
+
-
+
-
  public PayController() {
+
-
    initialize(
+
-
      VideoShop.getCurrency(),
+
-
      new MoneyBagImpl("moneyPayed", VideoShop.getCurrency(), true),
+
-
      new CCSStrategy(),
+
-
      null);
+
-
  }
+
-
 
+
-
  @RequestMapping("")
+
-
  public ModelAndView index(ModelAndView mav, HttpServletRequest request, HttpServletResponse response) {
+
-
 
+
-
    Value pricePaid = calculatePricePaid();
+
-
    Value priceToPay = calculatePriceToPay();
+
-
   
+
-
    mav.addObject("pricePayedEnough", pricePaid.compareTo(priceToPay) >= 0);
+
-
 
+
-
    MessagesUtil.addFlash(mav, messageSource.getMessage("videoautomat.pricePayed",
+
-
                                                        new String[{""+pricePaid,
+
-
                                                                    ""+priceToPay,
+
-
                                                                    EUROCurrencyImpl.ABBREVIATION},
+
-
                                                        LocaleContextHolder.getLocale()));
+
-
    /* videoautomat.pricePayed = {0} of {1} {2} payed */
+
-
         
+
-
    mav.addObject("currency",new ATMBuilder<Currency<CurrencyItemImpl>>(getSource()).getATM());
+
-
 
+
-
    mav.addObject("moneyBag", new ATMBuilder<MoneyBag>(getDestination())
+
-
                              .zeros(false)
+
-
                              .getATM());
+
-
 
+
-
    mav.addObject("currencyExtraColumns", Collections.singletonList(new EuroCurrencyImageEC("50", null)));
+
-
 
+
-
    mav.setViewName("pay");
+
-
    return mav;
+
-
  }
+
-
 
+
-
  private Value calculatePricePaid() {
+
-
    return getDestination().sumStock(getDataBasket(), new CatalogItemValue(), new DoubleValue(0)).divide(new IntegerValue(100));
+
-
  }
+
-
 
+
-
  private Value calculatePriceToPay() {
+
-
    ???
+
-
  }
+
-
}
+
-
</code>
+
-
Ein weiterer konkreter DoubleViewController: diesmal im Konstruktor initialisiert, da wir kein Request-Objekt benötigen. Der PayController verschiebt aus einem speziellen Geldkatalog "Currency" (sp incl.) in einen speziellen CountingStock "MoneyBag" (sp incl.) und symbolisiert das Einwerfen von Geldscheinen/-münzen in den Automat.
+
-
 
+
-
Innerhalb der ''index()''-Methode berechnen wir den bereits gezahlten Preis bequem über die mittels ''sumStock()''-Methode. Den zu zahlenden Betrag allerdings lässt sich nur mit der Information was ausgeliehen werden soll berechnen. Wir benötigen also den DataBasket aus dem RentController. Hierzu bedienen wir uns wieder der ''@Autowired''-Annotation und lassen uns den RentController automatisch injizieren. Das geht, weil dieser mit ''@Controller'' annotiert und somit von Spring instanziiert wurde.
+
-
 
+
-
<code java>
+
-
public class PayController extends DoubleViewController<Currency<CurrencyItemImpl>, MoneyBag> {
+
-
 
+
-
  @Autowired
+
-
  private RentController rentController;
+
-
+
-
  public void setRentController(RentController rentController) {
+
-
    this.rentController = rentController;
+
-
  }
+
-
  .
+
-
  .
+
-
  private Value calculatePriceToPay() {
+
-
    return rentController.getDataBasket().sumBasket(
+
-
            DataBasketConditionImpl.allStockItemsWithDest(rentController.getDestination()),
+
-
            new BasketEntryValue() {
+
-
              @Override
+
-
              public Value getEntryValue(DataBasketEntry dbe) {
+
-
                return ((VideoCassette)dbe.getValue()).getAssociatedItem(null).getValue();
+
-
              }
+
-
            },
+
-
            new DoubleValue(0));
+
-
  }
+
-
}
+
-
</code>
+
-
Nun können wir über den DataBasket fast genauso komfortabel summieren: "fast", weil eine Mapperklasse mitgegeben werden muss, dessen einzige Aufgabe es ist, das zu summierende ''Value'' aus dem DataBasketEntry herauszuholen.
+
-
 
+
-
In der ModelMap stehen nun neben der FlashMessage, wieviel noch gezahlt werden muss, ein boolscher Wert "pricePayedEnough", der ''true'' wird, wenn genug gezahl wurde, sowie das ATM des Geldkatalogs und das des bereits eingezahlten Geldes. Neu hierbei sind die ''ExtraColumn''s (EC), die als Alternative bzw. Erweiterung neben den bereits bekannten TEDs benutzt werden können, um eine bzw. mehrere Zusatzspalten hinzurendern. ECs werden in einer Liste verpackt direkt dem ViewTag übergeben. Die verwendete EC benutzt die ImageTag-Klasse, die sonst aus dem JSP-Context heraus als JSP-Tag benutzt wird, beispielhaft im JavaSourceCode, um zur jeweiligen Geldeinheit das entsprechende Bild zu rendern.
+
-
<code java>
+
-
public class EuroCurrencyImageEC extends AbstractExtraColumn<CurrencyItemImpl> {
+
-
 
+
-
  private String height;
+
-
  private String width;
+
-
 
+
-
  public EuroCurrencyImageEC(String height, String width) {
+
-
    super("x", null);
+
-
    this.height = height;
+
-
    this.width = width;
+
-
  }
+
-
 
+
-
  @Override
+
-
  public String getCellContent(CurrencyItemImpl identifier) {
+
-
    ImageTag tag = new ImageTag();
+
-
    tag.setImage(identifier.getImage());
+
-
    tag.setAlt(identifier.getName());
+
-
    tag.setHeight(height);
+
-
    tag.setWidth(width);
+
-
    return tag.render().toString();
+
-
  }
+
-
}
+
-
</code>
+
-
Die ''WebContent/jsp/pay.jsp'' könnte so aussehen:
+
-
<code xml>
+
-
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+
-
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
+
-
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+
-
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
+
-
<%@ taglib uri="http://www.salespoint-framework.org/web/taglib" prefix="sp" %>
+
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+
-
<html xmlns="http://www.w3.org/1999/xhtml">
+
-
  <head>
+
-
    <meta http-equiv="Content-Type" content="text/html; charset=uft-8" />
+
-
    <link rel="stylesheet" type="text/css" href="<c:url value="/static/resources/css/style.css" />" />
+
-
    <title><spring:message code="videoautomat.pay" /></title>
+
-
  </head>
+
-
  <body>
+
-
 
+
-
    <sp:Messages messages="${spErrors}" />
+
-
    <sp:Messages messages="${spFlash}" />
+
-
+
-
    <sp:LoggedIn status="true">
+
-
 
+
-
      <sp:DoubleView showNumberField="true" >
+
-
        <sp:List abstractTableModel="${currency}" extraCols="${currencyExtraColumns}"  />
+
-
        <sp:Table abstractTableModel="${moneyBag}" />
+
-
      </sp:DoubleView>
+
-
+
-
      <form:form method="get" action="pay/success" id="success">
+
-
        <c:if test="${pricePayedEnough}">
+
-
          <input type="submit" value="<spring:message code="videoautomat.pay" />" />
+
-
        </c:if>
+
-
        <c:if test="${!pricePayedEnough}">
+
-
          <input disabled="disabled" type="submit" value="<spring:message code="videoautomat.pay" />" />
+
-
        </c:if>
+
-
      </form:form>
+
-
      <form:form method="get" action="pay/cancel" id="cancel">
+
-
        <input type="submit" value="<spring:message code="videoautomat.cancel" />" />
+
-
      </form:form>
+
-
 
+
-
    </sp:LoggedIn>
+
-
 
+
-
  </body>
+
-
</html>
+
-
</code>
+
-
Neu hierbei ist die Verwendung von Kontrollstrukturen aus der JSTL-TagLibrary (''c''-Namespace) mit dessen Hilfe hier der boolsche Wert, ob genug bezahlt worden ist, abgeprüft wird und entsprechend der ''pay''-Button de-/aktiviert wird.
+
-
 
+
-
Der Pay- sowie Cancel-Button brauchen natürlich noch entsprechende Actions im PayController:
+
-
<code java>
+
-
  @RequestMapping("/success")
+
-
  public ModelAndView success(ModelAndView mav, HttpServletRequest request) {
+
-
    /* videoautomat.rent.successful = rent was process successful, payback is {0} EUR */
+
-
    MessagesUtil.addFlash(mav, messageSource.getMessage("videoautomat.rent.successful",
+
-
                                                        new Object[]{calculatePricePaid().subtract(calculatePriceToPay()),
+
-
                                                                    EUROCurrencyImpl.ABBREVIATION},
+
-
                                                        RequestContextUtils.getLocale(request)));
+
-
    /* add videocassettes to uservideostock */
+
-
    rentController.getDataBasket().commit();
+
-
 
+
-
    /* clean the moneybag by setting fresh instance */
+
-
    setDestination(new MoneyBagImpl("moneyPayed", VideoShop.getCurrency(), true));
+
-
 
+
-
    AutomatUser currentUser = (AutomatUser) UserManager.getInstance().getCurrentUser(request.getSession());
+
-
    mav.addObject("currentUserVideoStockATM", new ATMBuilder<UserVideoStock>(currentUser.getVideoStock()).getATM());
+
-
    mav.setViewName("rentSuccessful");
+
-
    return mav;
+
-
  }
+
-
+
-
  @RequestMapping("/cancel")
+
-
  public ModelAndView cancel(ModelAndView mav, HttpServletRequest request) {
+
-
    setDestination(new MoneyBagImpl("moneyPayed", VideoShop.getCurrency(), true));
+
-
    return rentController.cancel(mav, request);
+
-
  }
+
-
</code>
+
-
Die ''success''-Methode bestätigt den erfolgreichen Ausleihvorgang inkl. Rückgeldbetrag mit einer Erfolgsnachricht und ''committ''et den DataBasket vom RentController (verschiebt die VideoCassette-Instanzen persistent vom AutomatVideoStock zum UserVideoStock). '''Achtung:''' Außerdem setzt sie eine neue MoneyBag-Instanz - das ist wichtig, denn ein Controller mit Session-Scope bleibt erhalten bis die Sission endet(ServerEinstellung), d.h. beim eventl. 2.Ausleihvorgang während der gleichen Session wäre sonst der Bezahlbetrag vom 1.Ausleihvorgang noch enthalten!
+
-
Abschließend soll in der JSP der UserVideoStock angezeigt werden, also holen wir uns den User der aktuellen Session und fügen ein mittels ATMBuilder erstelltes ATM von seinem UserVideoStock der ModelMap hinzug.
+
-
 
+
-
''WebContent/jsp/rentSuccessful.jsp'':
+
-
<code xml>
+
-
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+
-
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
+
-
<%@ taglib uri="http://www.salespoint-framework.org/web/taglib" prefix="sp" %>
+
-
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
+
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+
-
<html xmlns="http://www.w3.org/1999/xhtml">
+
-
  <head>
+
-
    <meta http-equiv="Content-Type" content="text/html; charset=uft-8" />
+
-
    <link rel="stylesheet" type="text/css" href="<c:url value="/static/resources/css/style.css" />" />
+
-
    <title>SalesPoint2010-BlankWebapplication</title>
+
-
  </head>
+
-
  <body>
+
-
 
+
-
    <sp:Messages messages="${spErrors}" styleName="error"/>
+
-
    <sp:Messages messages="${spFlash}" styleName="flash"/>
+
-
+
-
    <sp:LoggedIn status="true">
+
-
      <sp:Table abstractTableModel="${currentUserVideoStockATM}" />
+
-
    </sp:LoggedIn>
+
-
 
+
-
    <a href="<c:url value="/"/>">back</a>
+
-
 
+
-
  </body>
+
-
</html>
+
-
</code>
+
=Abschlussbetrachtung=
=Abschlussbetrachtung=
In diesem Tutorial wurden die wichtigsten Punkte anhand des Videoautomatenbeispiels angesprochen. Das runterladbare Webprojekt enthält ein paar weitere Funktionalitäten wie z.B. das Zurückgeben von Videos, eine Nutzerprofilseite sowie eine Adminseite. Ein Blick in diesen Quellcode sollte bis auf die zusätzliche Nutzung von CSS nichts Neues offenbaren.
In diesem Tutorial wurden die wichtigsten Punkte anhand des Videoautomatenbeispiels angesprochen. Das runterladbare Webprojekt enthält ein paar weitere Funktionalitäten wie z.B. das Zurückgeben von Videos, eine Nutzerprofilseite sowie eine Adminseite. Ein Blick in diesen Quellcode sollte bis auf die zusätzliche Nutzung von CSS nichts Neues offenbaren.

Version vom 16:08, 7. Okt. 2010

comment5, http://k8cvsn1.pochtamt.ru/site-464.html ñêà÷àòü èãðû ãîíêè íà nokia 6300, ohvl, http://kiddcorpchanma.szm.com/site-497.html ñêà÷àòü áåñïëàòíî àóäèî îáó÷åíèå àíãëèéñêîìó, =-PP, http://keyposihan.chez.com/page-345.html ñ.ìèíàåâ àññîëü ñêà÷àòü ïåñíþ, gjl, http://leobramamrak.chez.com/klip-faina-skachat.html êëèï ôàèíà ñêà÷àòü, klsxy, http://kirabodhcar.szm.com/page-430.html ÿ óñòàë îò äîðîã ñêà÷àòü, 480080,

comment5, http://lignvenicar.szm.com/page_462.html îðãàíèçàöèÿ êàê ôóíêöèÿ ìåíåäæìåíòà äàéòå ñêà÷àòü, =[[, http://lisynleguar.chez.com/page_441.html ñêà÷àòü áåñïëàòíî àëüáîìû ãðóïïû êàáðèîëåò, trtymc, http://keyposihan.chez.com/site-279.html ñêà÷àòü îïåðó íà êîìïüþòåð ÷åðåç òåëåôîí, wtb, http://knoljesmidis.szm.com/doc_432.html ïðîãðàììà ïî ïîäáîðó íàñîñà ñêà÷àòü, 92248, http://lignvenicar.szm.com/page_464.html ýðîòè÷åñêèå ìóëüòèêè áåëîñíåæêà ñêà÷àòü, =-[[[, http://lignvenicar.szm.com/page_232.html ñêà÷àòü ïåñíè âåðû êóøíèð, 2050,

comment5, http://lbizkinrekil.chez.com/site-197.html ãóô è àéçà-ïðèçíàíèå ñêà÷àòü áåñïëàòíî, rrencl, http://libtastvenli.szm.com/doc_107.html ðåôåðàò áèîëîãèÿ ïðèíöèïû êëàññèôèêàöèè âèäîâ ñêà÷àòü, qyaljp, http://lignvenicar.szm.com/page-398.html toxic áðèòíè ñêà÷àòü avi, frqvb, http://lisynleguar.chez.com/page-92.html ïóøêè îñòðîâà íàâàðîí ôèëüì ñêà÷àòü, cdwdex, http://knowantota.szm.com/site-115.html ïåñíÿ è ñàðóõàíîâà ñêà÷àòü, 039906, http://lechanrochess.chez.com/skachat-besplatno-inek-analitik.html ñêà÷àòü áåñïëàòíî èíýê-àíàëèòèê, 015, http://lobswelersver.chez.com/skachat-player-baby-come-back.html ñêà÷àòü player baby come back, 026818,

Abschlussbetrachtung

In diesem Tutorial wurden die wichtigsten Punkte anhand des Videoautomatenbeispiels angesprochen. Das runterladbare Webprojekt enthält ein paar weitere Funktionalitäten wie z.B. das Zurückgeben von Videos, eine Nutzerprofilseite sowie eine Adminseite. Ein Blick in diesen Quellcode sollte bis auf die zusätzliche Nutzung von CSS nichts Neues offenbaren.

Persönliche Werkzeuge