Een locatie kan informatie verrijken en met de opkomst van Google Maps en concurrenten wordt daar op internet veel gebruik van gemaakt. Bij menig online service kun je een event, foto of bericht koppelen aan een locatie. Dat is op zich ook niet zo gek als je ziet hoe makkelijk Google het ons gemaakt heeft. Er is een uitgebreide en goed gedocumenteerde API om Google Maps ook op jouw website een plekje te geven.
Afgelopen jaar probeerde ik voor een project Google Maps te integreren in een Java programma. Dat viel nog niet mee. Dus de vraag waar ik nu mee zit is of Google Maps te integreren is binnen GWT.
Het antwoord zal je niet verrassen, natuurlijk kan dat en het is nog makkelijker dan ik verwacht had. Ik ga hieronder niet uitleggen hoe het werkt, maar verwijs naar een tutorial van Google waar het erg helder uit staat gewerkt.
http://code.google.com/p/gwt-google-apis/wiki/MapsGettingStarted
En mijn eigen probeersel:
http://waalwijk.yall.nl:8080/GoogleMaps/
woensdag 24 maart 2010
woensdag 17 maart 2010
GWT en Comet
Lang geleden, toen het world wide web uitgedacht werd, is er weinig rekening gehouden met de behoefte die de gebruikers later zouden krijgen. Er is een protocol bedacht (HTTP) waarmee een client, meestal een browser, een bestand op kan vragen. Een webserver antwoord dan door het bestand terug te sturen. Een aanvraag kan eventueel voorzien worden van extra informatie in de vorm van key-value pairs. Later is er nog een kleine aanpassing aan dit HTTP protocol geweest om het wat efficienter te laten werken wanneer er meerdere bestanden binnen gehaald moeten worden. Verdere aanpassingen zijn uitgebleven.
Met de opkomst van JavaScript in de browser, en daarmee AJAX, werd het mogelijk om informatie ook op een andere manier naar de browser te sturen. Niet langer moest keer op keer een volledige pagina worden gedownload, maar alleen de nieuwe informatie kon opgehaald worden in een asynchrone request. Wachttijden zijn hierdoor drastisch verlaagd. Het principe is echter niet veranderd. Nog steeds wordt er een bestand opgevraagd en ontvangen. Enkel de verwerking van dit bestand in de browser is gewijzigd.
Maar wat nu als je web applicatie afhankelijk is van server updates? Het is dan erg onhandig om de server periodiek te vragen of er misschien al een update is. Ten eerste zit er dan bijna altijd een vertraging in en ten tweede zullen er veel lege berichten op en neer gestuurd worden op de momenten dat er geen updates zijn. Helaas hebben we hier te maken met restricties in het HTTP protocol.
Zoals het altijd gaat in de software wereld, is er toch altijd wel weer iemand met een briljante ingeving die een loophole ontdekt in het protocol. Zo ook in dit geval. Wanneer je een webrequest doet, heb je vaak in no time het antwoord binnen. Maar zo was het niet altijd. Toen het internet nog wat trager was, deden berichten er wat langer over. Een browser moest dan maar wachten tot er een keer een antwoord kwam. Het is moeilijk vast te stellen of er nog niet lang genoeg is gewacht, of dat er iets mis is gegaan. Daarom is afgesproken dat er na een instelbare hoeveelheid tijd, vanuit gegaan mag worden dat er iets mis is gegaan. Er kan dan een nieuw request verstuurd worden. Dit principe is uitgebuit in een techniek die later PTTH, Reverse AJAX , of Comet (een ander populair Amerikaans schoonmaak middel naast Ajax) genoemd is.
Het idee is het volgende. Een client doet een request, maar hij krijgt geen antwoord. De server houdt de lijn open. Er kunnen twee dingen gebeuren. Er verschijnt een update op de server, welke opgestuurd kan worden naar de client. De openstaande lijn kan hiervoor worden gebruikt. Het tweede dat kan gebeuren is dat er geen update beschikbaar is voordat de timeout op gaat treden. In dat geval wordt er een leeg antwoord gestuurd. De client doet vervolgens een nieuwe aanvraag waardoor het proces opnieuw begint. De twee nadelen die aanwezig waren bij het polling mechanisme, worden hier verkleind. Updates worden meteen naar de client gestuurd en het aantal lege berichten is minimaal. Comet is niet nieuw, maar bestaat al een aantal jaren, al is het niet bij iedereen even bekend.
Kunnen we deze techniek nu ook gebruiken binnen GWT? Natuurlijk. En het mooie is, het vervelende werk is al gedaan door andere mensen. Ik heb gezocht naar libraries en na enkele uren lezen en spelen, ben ik blijven steken bij een project genaamd GWTEventService. Van alles dat ik geprobeerd heb, werkte deze toch het lekkerst. Een client kan zich inschrijven op een event type, en de server kan een event van dit type publiceren. Er gaat dan bij de client, mits hij geabonneerd is op dit event-type, vrijwel meteen een event af.
Tijd voor een voorbeeld. Stel we hebben twee simpele chat Widgets (toch weer een chat voorbeeld) die volledig onafhankelijk van elkaar zijn. Beide kunnen ze een berichtje sturen naar de server en dit berichtje moet dan meteen doorgestuurd worden naar de andere chat Widgets.
Maak een nieuw project aan, download de jar-file van GWTEventService en voeg deze toe aan je build-path. Voeg ook de volgende regel toe in het .gwt.xml configuratiebestand.
De volgende stap is het implementeren van het event dat we willen versturen.
De volgende stap is het schrijven van de standaard RPC interface zoals dat binnen GWT gebruikelijk is. We definieren een methode waarmee een chat client een berichtje kan sturen.
En zoals gewoonlijk hebben we ook de asynchrone variant.
Deze methodes moeten we implementeren aan de server kant.
Vervolgens moeten we enkel de client nog implementeren. Om het een beetje overzichtelijk te houden, heb ik alleen de essentiele dingen achter gelaten.
Tenslotte moeten we de servlet nog toevoegen aan de configuratie file voor de applicatie server (Web.xml)
Het gebrek aan de mogelijkheid tot pushen van gegevens wordt vaak als een nadeel gezien van web applicaties, maar met de techniek die hierboven beschreven staat, hoeft dat dus niet zo te zijn.
Succes met spelen.
Gebruikte libraries:
http://code.google.com/p/gwteventservice/
Het voorbeeld:
http://waalwijk.yall.nl:8080/EventService/
Met de opkomst van JavaScript in de browser, en daarmee AJAX, werd het mogelijk om informatie ook op een andere manier naar de browser te sturen. Niet langer moest keer op keer een volledige pagina worden gedownload, maar alleen de nieuwe informatie kon opgehaald worden in een asynchrone request. Wachttijden zijn hierdoor drastisch verlaagd. Het principe is echter niet veranderd. Nog steeds wordt er een bestand opgevraagd en ontvangen. Enkel de verwerking van dit bestand in de browser is gewijzigd.
Maar wat nu als je web applicatie afhankelijk is van server updates? Het is dan erg onhandig om de server periodiek te vragen of er misschien al een update is. Ten eerste zit er dan bijna altijd een vertraging in en ten tweede zullen er veel lege berichten op en neer gestuurd worden op de momenten dat er geen updates zijn. Helaas hebben we hier te maken met restricties in het HTTP protocol.
Zoals het altijd gaat in de software wereld, is er toch altijd wel weer iemand met een briljante ingeving die een loophole ontdekt in het protocol. Zo ook in dit geval. Wanneer je een webrequest doet, heb je vaak in no time het antwoord binnen. Maar zo was het niet altijd. Toen het internet nog wat trager was, deden berichten er wat langer over. Een browser moest dan maar wachten tot er een keer een antwoord kwam. Het is moeilijk vast te stellen of er nog niet lang genoeg is gewacht, of dat er iets mis is gegaan. Daarom is afgesproken dat er na een instelbare hoeveelheid tijd, vanuit gegaan mag worden dat er iets mis is gegaan. Er kan dan een nieuw request verstuurd worden. Dit principe is uitgebuit in een techniek die later PTTH, Reverse AJAX , of Comet (een ander populair Amerikaans schoonmaak middel naast Ajax) genoemd is.
Het idee is het volgende. Een client doet een request, maar hij krijgt geen antwoord. De server houdt de lijn open. Er kunnen twee dingen gebeuren. Er verschijnt een update op de server, welke opgestuurd kan worden naar de client. De openstaande lijn kan hiervoor worden gebruikt. Het tweede dat kan gebeuren is dat er geen update beschikbaar is voordat de timeout op gaat treden. In dat geval wordt er een leeg antwoord gestuurd. De client doet vervolgens een nieuwe aanvraag waardoor het proces opnieuw begint. De twee nadelen die aanwezig waren bij het polling mechanisme, worden hier verkleind. Updates worden meteen naar de client gestuurd en het aantal lege berichten is minimaal. Comet is niet nieuw, maar bestaat al een aantal jaren, al is het niet bij iedereen even bekend.
Kunnen we deze techniek nu ook gebruiken binnen GWT? Natuurlijk. En het mooie is, het vervelende werk is al gedaan door andere mensen. Ik heb gezocht naar libraries en na enkele uren lezen en spelen, ben ik blijven steken bij een project genaamd GWTEventService. Van alles dat ik geprobeerd heb, werkte deze toch het lekkerst. Een client kan zich inschrijven op een event type, en de server kan een event van dit type publiceren. Er gaat dan bij de client, mits hij geabonneerd is op dit event-type, vrijwel meteen een event af.
Tijd voor een voorbeeld. Stel we hebben twee simpele chat Widgets (toch weer een chat voorbeeld) die volledig onafhankelijk van elkaar zijn. Beide kunnen ze een berichtje sturen naar de server en dit berichtje moet dan meteen doorgestuurd worden naar de andere chat Widgets.
Maak een nieuw project aan, download de jar-file van GWTEventService en voeg deze toe aan je build-path. Voeg ook de volgende regel toe in het .gwt.xml configuratiebestand.
<inherits name="de.novanic.eventservice.GWTEventService" />
De volgende stap is het implementeren van het event dat we willen versturen.
import de.novanic.eventservice.client.event.Event;
import de.novanic.eventservice.client.event.domain.Domain;
import de.novanic.eventservice.client.event.domain.DomainFactory;
public class MyEvent implements Event
{
/**
* This distinguishes the event from others
*/
public static final Domain NS = DomainFactory.getDomain("server_message_domain");
private String message;
private String sender;
/**
* Needed for serialization
*/
public MyEvent() {}
public MyEvent(String sender, String message) {
this.sender = sender;
this.message = message;
}
public String getMessage() {
return message;
}
public String getSender() {
return sender;
}
}
De volgende stap is het schrijven van de standaard RPC interface zoals dat binnen GWT gebruikelijk is. We definieren een methode waarmee een chat client een berichtje kan sturen.
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
@RemoteServiceRelativePath("MyInterface")
public interface MyInterface extends RemoteService {
public void say(String who, String what);
}
En zoals gewoonlijk hebben we ook de asynchrone variant.
import com.google.gwt.user.client.rpc.AsyncCallback;
public interface MyInterfaceAsync {
public void say(String who, String what, AsyncCallback anAsyncCallback);
}
Deze methodes moeten we implementeren aan de server kant.
import nl.yall.test.eventservice.client.MyEvent;
import nl.yall.test.eventservice.client.MyInterface;
import de.novanic.eventservice.service.RemoteEventServiceServlet;
public class MyInterfaceImpl extends RemoteEventServiceServlet implements MyInterface {
public void say(String who, String what) {
MyEvent event = new MyEvent(who, what);
addEvent(MyEvent.NS, event);
}
}
Vervolgens moeten we enkel de client nog implementeren. Om het een beetje overzichtelijk te houden, heb ik alleen de essentiele dingen achter gelaten.
import de.novanic.eventservice.client.event.Event;
import de.novanic.eventservice.client.event.RemoteEventService;
import de.novanic.eventservice.client.event.RemoteEventServiceFactory;
import de.novanic.eventservice.client.event.listener.RemoteEventListener;
public class EventService implements EntryPoint {
private final MyInterfaceAsync service =
GWT.create(MyInterface.class);
private RemoteEventService remoteService =
RemoteEventServiceFactory.getInstance().getRemoteEventService();
public void onModuleLoad() {
HorizontalPanel panel = new HorizontalPanel();
panel.add(new MyPanel("Gerben"));
panel.add(new MyPanel("David"));
RootLayoutPanel.get().add(panel);
}
private class MyPanel extends FlowPanel {
private final TextBox messageBox = new TextBox();
private final ListBox listBox = new ListBox(true);
private final String user;
public MyPanel(final String user) {
this.user = user;
setSize("60%", "60%");
remoteService.addListener(MyEvent.NS, new RemoteEventListener() {
public void apply(Event e) {
if(e instanceof MyEvent) {
MyEvent me = (MyEvent) e;
listBox.addItem(me.getSender() + ": " + me.getMessage());
}
}
});
messageBox.addKeyPressHandler(new KeyPressHandler() {
@Override
public void onKeyPress(KeyPressEvent event) {
if (event.getCharCode() == '\r') {
sendMessage();
messageBox.setText("");
}
}
});
listBox.setSize("100%", "80%");
listBox.addItem("Listening...");
add(new Label(user));
add(listBox);
add(messageBox);
}
private void sendMessage() {
service.say(user, messageBox.getText(), new AsyncCallback() {
public void onFailure(Throwable aThrowable) {}
public void onSuccess(Void aResult) {}
});
}
}
}
Tenslotte moeten we de servlet nog toevoegen aan de configuratie file voor de applicatie server (Web.xml)
<servlet> <servlet-name>EventService</servlet-name> <servlet-class>de.novanic.eventservice.service.EventServiceImpl</servlet-class> </servlet> <servlet-mapping> <servlet-name>EventService</servlet-name> <url-pattern>/eventservice/gwteventservice</url-pattern> </servlet-mapping> <servlet> <servlet-name>EventServlet</servlet-name> <servlet-class>nl.yall.test.eventservice.server.MyInterfaceImpl</servlet-class> </servlet> <servlet-mapping> <servlet-name>EventServlet</servlet-name> <url-pattern>/eventservice/MyInterface</url-pattern> </servlet-mapping>
Het gebrek aan de mogelijkheid tot pushen van gegevens wordt vaak als een nadeel gezien van web applicaties, maar met de techniek die hierboven beschreven staat, hoeft dat dus niet zo te zijn.
Succes met spelen.
Gebruikte libraries:
http://code.google.com/p/gwteventservice/
Het voorbeeld:
http://waalwijk.yall.nl:8080/EventService/
woensdag 24 februari 2010
GWT Widget
Het grafische deel van een GWT applicatie bestaat uit Panels en Widgets. Een Panel is een container die andere Panels of Widgets kan bevatten. Verschillende panels rangschikken hun inhoud op verschillende manieren (links naar rechts, boven naar beneden, etc). Een Widget is een user-interface element. Standaard hebben we hier de TextBox, Button, Checkboxen etc. Daarnaast kunnen we ook zelf Widgets bouwen.
Stel nu dat we zo'n Widget hebben gebouwd en we willen die graag gebruiken op onze website, zonder alles om te bouwen naar GWT. Dit gaat als volgt. We voegen aan onze bestaande website een element toe:
Op zich is dit nog niet zo spannend, dus we gaan een stap verder. In het volgende voorbeeld gebruiken we een uitgebreide versie van de RichTextArea, voorzien van een toolbar. Deze Widget willen we voortaan gebruiken als standaard textarea. We maken het volgende html bestand:
Stel nu dat we zo'n Widget hebben gebouwd en we willen die graag gebruiken op onze website, zonder alles om te bouwen naar GWT. Dit gaat als volgt. We voegen aan onze bestaande website een element toe:
<div id="mijnwidget"></div>Dit is de placeholder waar onze widget gaat komen. Start nu een nieuw GWT project en implementeer de onModuleLoad() methode als volgt:
public void onModuleLoad() {
Widget widget = new MijnWidget();
RootPanel.get("mijnwidget").add(widget);
}
Compileer je project en zet de directory met daarin het resultaat naast je oorspronkelijke HTML bestand. Voeg tenslotte de volgende regel toe binnen de <head> en </head> tags:<script type="text/javascript" language="javascript" src="mywidget/mywidget.nocache.js"></script>waarin mywidget verwijst naar het door GWT gegenereerde JS bestand. Nu zou het moeten werken.
Op zich is dit nog niet zo spannend, dus we gaan een stap verder. In het volgende voorbeeld gebruiken we een uitgebreide versie van de RichTextArea, voorzien van een toolbar. Deze Widget willen we voortaan gebruiken als standaard textarea. We maken het volgende html bestand:
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link type="text/css" rel="stylesheet" href="MyExample.css">
<title>RichTextArea Demo</title>
<script type="text/javascript" language="javascript" src="myexample/myexample.nocache.js"></script>
</head>
<body>
<form action="myexample/print" method="post">
<textarea name="mytext" class="rich"></textarea><br />
<input type="submit" />
</form>
</body>
</html>
Nu moeten we nog een script maken dat onze Widget over de bovenstaande textarea heenplakt. Dit doen we als volgt. We gaan eerst op zoek naar de elementen waar onze Widget op geplakt moet worden. Deze elementen slaan we op. Vervolgens vervangen we deze elementen door placeholders. Vervolgens zetten we op de plaats van deze placeholder een kopie van onze Widget en een hidden input element waarin we de waarde van de textarea opslaan. Als de waarde in de textarea verandert, moet ook de waarde in onze hidden inputbox mee veranderen. En voila, onze eigen input widget.public class MyExample implements EntryPoint {
private Vector richTextAreas = new Vector();
public void onModuleLoad() {
Element body = RootPanel.getBodyElement();
NodeList inputs = body.getElementsByTagName("textarea");
for (int i = 0; i < inputs.getLength(); i++) {
Element element = inputs.getItem(i);
if (element instanceof TextAreaElement) {
TextAreaElement textAreaElement = (TextAreaElement) element;
if (textAreaElement.getClassName().startsWith("rich")) {
richTextAreas.add(textAreaElement);
}
}
}
for (TextAreaElement element : richTextAreas) {
Element parent = element.getParentElement();
String id = generateId();
Element el = new Label().getElement();
el.setId(id);
parent.replaceChild(el, element);
replaceTextArea(id, element.getName());
}
}
private void replaceTextArea(String id, String name) {
final Hidden hidden = new Hidden();
hidden.setName(name);
final RichTextArea textArea =
new RichTextArea();
textArea.addRichTextHandler(new RichTextHandler() {
@Override
public void onChange(RichTextEvent event) {
hidden.setValue(event.getHtml());
}});
RootPanel.get(id).clear();
RootPanel.get(id).add(textArea);
RootPanel.get(id).add(hidden);
}
private String generateId() {
return UUID.uuid();
}
}
An example can be found here.
woensdag 17 februari 2010
GWT 2.0
In het kader van persoonlijke ontwikkeling heb ik met mijn werkgever afgesproken om komend jaar één dag per week te besteden aan het leren van nieuwe (werk gerelateerde) vaardigheden. Tijdens het normale werk is er vaak geen tijd om ergens wat dieper in te duiken, of om een onbekende techniek, die wellicht heel handig zou kunnen zijn, te onderzoeken. Tijdens mijn nieuwe dag in de week neem ik de tijd om met deze onbekende technieken te experimenteren. In deze blog wil ik zo nu en dan kort vertellen wat ik geleerd of gemaakt heb.
Om die reden ben ik kort geleden weer eens aan de slag gegaan met Google Web Toolkit 2.0 (GWT). GWT is een Java-framework dat het mogelijk maakt om JavaScript-applicaties te ontwikkelen in een Java omgeving. Het maken van wat exotischere JavaScript-applicaties is vervelend omdat de verschillende webbrowsers eigen JavaScript engines (en HTML renderers hebben). De ene fabrikant volgt de JavaScript standaard stricter dan de andere, waardoor er kleine verschillen zijn ontstaan. Een JavaScript-applicatie kan zich daarom op verschillende manieren gedragen in verschillende browsers. De programmeur moet hier rekening mee houden door in sommige gevallen functionaliteit op verschillende manieren te programmeren. Daarnaast is het debuggen van een JavaScript applicatie een vervelende klus.
GWT geeft ons een omgeving waarin we op een hoger niveau een applicatie kunnen ontwikkelen. Gebruikmakend van libraries en tools van Google, kun je je webapplicatie helemaal in Java ontwikkelen, debuggen en testen. Wanneer je klaar bent kun je je code omzetten naar JavaScript-code. Tijdens deze stap wordt er rekening gehouden met de browser verschillen. De ontwikkelaar is daarom sneller klaar, of kan in dezelfde tijd een geliktere applicatie in elkaar zetten.
GWT had lang last van kinderziektes en onvolledigheden, maar inmiddels kunnen we bij versie 2.0 zeggen dat GWT volwassen is geworden. Ik kan daarom iedereen die bezig is met webapplicaties aanraden er eens naar te kijken.
Om die reden ben ik kort geleden weer eens aan de slag gegaan met Google Web Toolkit 2.0 (GWT). GWT is een Java-framework dat het mogelijk maakt om JavaScript-applicaties te ontwikkelen in een Java omgeving. Het maken van wat exotischere JavaScript-applicaties is vervelend omdat de verschillende webbrowsers eigen JavaScript engines (en HTML renderers hebben). De ene fabrikant volgt de JavaScript standaard stricter dan de andere, waardoor er kleine verschillen zijn ontstaan. Een JavaScript-applicatie kan zich daarom op verschillende manieren gedragen in verschillende browsers. De programmeur moet hier rekening mee houden door in sommige gevallen functionaliteit op verschillende manieren te programmeren. Daarnaast is het debuggen van een JavaScript applicatie een vervelende klus.
GWT geeft ons een omgeving waarin we op een hoger niveau een applicatie kunnen ontwikkelen. Gebruikmakend van libraries en tools van Google, kun je je webapplicatie helemaal in Java ontwikkelen, debuggen en testen. Wanneer je klaar bent kun je je code omzetten naar JavaScript-code. Tijdens deze stap wordt er rekening gehouden met de browser verschillen. De ontwikkelaar is daarom sneller klaar, of kan in dezelfde tijd een geliktere applicatie in elkaar zetten.
GWT had lang last van kinderziektes en onvolledigheden, maar inmiddels kunnen we bij versie 2.0 zeggen dat GWT volwassen is geworden. Ik kan daarom iedereen die bezig is met webapplicaties aanraden er eens naar te kijken.
vrijdag 8 mei 2009
Afgestudeerd
Het heeft lang geduurd. Het heeft veel te lang geduurd. Maar op 7 mei 2009 is het dan eindelijk gebeurd. Na een presentatie van 35 minuten, een vragen ronde van 45 minuten en een lange besloten discussie klonk het dan eindelijk: "Je bent geslaagd." Het onderzoek dat ik gedaan heb zal de wereld niet gaan veranderen, maar ik heb er tenminste een papiertje mee verdiend. Ik ben voorlopig uitgestudeerd. Tijd voor vakantie :)
Labels:
Life
zaterdag 28 maart 2009
Thesis opgestuurd
Een week of vijf geleden ontving ik mijn thesis terug van de professor. Het had even geduurd, maar dat was niet geheel onterecht, want zelf deed ik ook niet altijd mijn best om snel te reageren. Het was zelfs een keer zo erg dat de prof dacht dat ik het opgegeven had. Dat was natuurlijk niet zo. De meeste bladzijden van mijn thesis waren voorzien van een aardige doses rode pen. Aan mij de taak om er wat mee te doen. In de avonden en weekenden heb ik eraan gewerkt, en vandaag heb ik eindelijk een nieuwe versie van de thesis ingeleverd. Versie 1.00. Dus de bal ligt weer bij de prof. Nu maar hopen dat hij tevreden is en dat we binnenkort een einde kunnen plakken aan dit ellendige afstudeer avontuur.
Labels:
Life
zaterdag 20 september 2008
Afstuderen
Het is inmiddels alweer ongeveer een jaar geleden dat ze iets van mij vernomen hebben op de TU/e. Week na week begon het meer aan me te knagen en vandaag heb ik de knoop doorgehakt. Mijn verslag is goed genoeg om nog eens in te leveren. Vandaag heb ik de nieuwste versie naar mijn professor en begeleider gemailt. Misschien ga ik dan toch nog ooit afstuderen.
Labels:
Life
Abonneren op:
Posts (Atom)