Samstag, 31. Dezember 2011

Besuch im Legoland

Nachdem es hier jetzt einige Zeit sehr ruhig war, dachte ich mir ich poste mal etwas über unseren diesjährigen Besuch im Legoland Günzburg.

Für meine Freundin und mich war das bereits der zweite Besuch dort und wir sind mal wieder hellauf begeistert :-) Únd neeeeiin, das ist nicht nur was für Kinder :-p Aber natürlich muss man damit rechnen, dort auf viele Familien zu treffen, von daher bietet sich ein Besuch eher außerhalb der Ferienzeiten an.

Zu erwähnen sind zum Einen die zahlreichen Fahrgeschäfte die es in den verschiedenen Themenbereichen gibt (Ritter, Piraten, Bionicle, Abenteuer...), die alle unterschiedlich wild sind und so doch für so ziemlich jeden was passendes bieten. Hervorheben muss man da aus meiner Sicht besonders die Bioncles bei denen man einem großen Roboterarm sitzt den man zuvor selbst programmieren kann und damit die Fahrt selbst bestimmt. Das ist aber nichts für Leute mit schwachem Magen...

Außerdem ist die Wildwasserrutsche erwähnenswert die feuchtfröhlchen Spaß garantiert *g*




Weitere Attraktionen sind Goldwaschen, Führung durch die Legofactory und das Aquarium, letzteres bietet einen wirklich interessanten Rundgang durch eine aufwendig gestaltete Unterwasserlandschaft mit vielen exotischen Fischen.

So weit, so gut, aber die, zumindest für uns, interesantesten Bereiche fehlen noch. Im Build- und Testcenter kann man das tun, was jeder Legofan gern tut... BAUEN!!! Es werden verschiedene Modelle mit unterschiedlichem Schwierigkeitsgrad angeboten, wobei die größte Schwierigkeit oft darin liegt auf den relativ klein kopierten Bauplänen alles richtig zu erkennen ;-) Trotzdem macht das basteln immer viel Spaß und man bekommt danach auch eine nette kleine Urkunde *g* Zusätzlich bietet das Testcenter auch Workshops an, z.B. für Mindstorms in den wir reinkamen. In dem Workshop konnte man eine Stunde lang einen bestehenden Roboter programmieren und etwas herumexperimentieren, als Spätfolge gab es dann zu Weihnachten bei uns ein Mindstorms-Set :-)

Aber das Highlight sind eigentlich immer die Miniaturwelten die einige der schönsten oder beeindruckendsten Bauwerke/Stadtkulissen Europas darstellen und inzwischen gibt es dort auch einen "Star Wars"-Bereich.

Ein Hingucker ist hier vor allem das Schloß Neuschwanstein:






Oder der Reichstag unserer Hauptstadt:


Und für Sportfans gibt es natürlich auch etwas :-)






Das Legoland bietet aber noch weit mehr als das was ich hier beschrieben habe, von daher sollte man sich einen Besuch dort wirklich ein mal überlegen. Mehr Bilder von unserem Besuch gibt es in meinem entsprechenden Picasa-Album.


Freitag, 23. September 2011

Adding Footnote Support to Wicket

During my current Wicket project I found that there is no built in support for adding footnotes to components. All you could do was adding the footnote and the corresponding description explicitly and as separate components. This is not very flexible or generic and when you start adding footnotes to panels but want to have the description show up in the containing page or in a different component of the same page.it gets very tedious to keep everything consistent by hand.

So once more I put my project work on hold and started some plumbing ;-). It took me about two days of trying and testing but finally I came up with a solution that serves all of my needs and should be rather easy to use. But I would also like to point out that I am aware this solution might not be what the wicket team would recommend (suggestions for improvement are as always welcome). But for me this is quite sufficient and should cover most real life use cases. For those interessted the source is available here.

Here a summary of the available features:

- programmatically add a footnote to a component
- programmatically process the descriptions for programmatically added components
- declaratively add labels with footnotes to a markup container
- declaratively add label with footnote descriptions for declaratively added footnotes
- declaratively add label with footnote descriptions for programmatically added footnotes
- use text or images as footnotes
- use separate style definitions for each footnote and description
- use freely defined wrapping tags for declaratively added footnote descriptions

As a first I took on the part of the solution concerning adding footnotes programmatically to later add support for declarative footnote creation on top of it.

For the programmatical solution I created two artifacts:

- a new behavior FootnoteBehavior.java, to be added to the component that is to be enriched with a footnote
- a new interface FootnoteAware.java, that is used to handle the descriptions for added footnotes

The behavior takes several argument:

/**
   * @param footNote
   *            The footnote text to be added to the component's model object
   *            as superscript and will additionally be wrapped with an anchor
   *            tag, e.g. 1 would result in 1
   * @param createImageTag
   *            If set to true the value of footNote
   *            is wrapped into a html img tag in which it is used as src
   *            attribute. This allows to use images for a footnote instead of
   *            regular text.
   * @param footNoteClass
   *            If this parameter is not empty it's value will be set as class
   *            attribute on the generated anchor tag.
   * @param footNoteText
   *            If this parameter is not empty a footnote description will be
   *            created.
   * @param fromResolver
   *            Indicates if this behavior was constructed by
   *            {@link FootnoteResolver}
   */

  public FootnoteBehavior(String footNote, boolean createImageTag, String footNoteClass, String footNoteText, boolean fromResolver)

The text representing the footNote is appended to the response after the component the behavior has been added to finished rendering. Additionally a footnote description is created as a Label and added to the first parent in the component's hierarchy that implements the FootnoteAware interface, the specifics of how this is done can be seen in the javadoc and source code of the classes. Then the FootnoteAware can render the descriptions as it needs to, e.g. as a PropertyListView.

This was already a nice effort but not yet sufficient for every day use as most components that use footnotes are Labels which are usually declaratively. So with the solution we have so far it would be necessary to add all those Labels by Java code which would be a drawback.

So we need support for declarativ footnotes which can be achieved by adding two resolvers:

- FootnoteResolver.java, to add a Label component with a FootnoteBehavior to the markup container
- FootnoteDescriptionResolver.java, to add a Label containing all footnote descriptions from the closest FootnoteAware parent

The first resolver simply takes it's the tag attributes and constructs a Label component as well as a FootnoteBehavior accordingly. The key attribute is used to determine the Label's content in the same way as a tag would, which means it is resolved via proeprty lookup. In addition all attributes are interpolated using the markup container's properties and/or nested tags. For further details please check javadoc and source code.

The second resolver looks for a FootnoteAware parent of it's container and if it finds one renders the contained footnote descriptions according to the tag attributes. Each description will be wrapped in a tag who's name can be defined via the tagname attribute and can optionally have a class attribute to allow futher styling flexibility.

I hope this summary is comprehensive enough as I tried to keep it rather short instead of copying the classes' source code. If you find any flaws or have suggestions I would be glad if you shared them. And of course everyone is welcome to copy and use the code provided here.



Mittwoch, 10. August 2011

Creating a Custom Wicket Tag Resolver

When building pages with Wicket the wicket:message tag is very handy for adding localized text to said page. What I don't like about it is the fact that you are limited to
using properties. The problem about that is when you need to use large portion of text a property file gets very bloated, very ugly to read and incredibly ugly when trying to add formatting. From a maintenance point of view resoruce should be
structured very clearly and especially when using xml properties an auto formatter is very handy, which can also cause ill formatted results.

On workaround is to split your text in several properties and then just add them one by one to your markup/components but this is very cumbersome. So I figured it would be cool if you were able to use
includes in the same way as properties with the message tag where you could externalize a layouted portion of text with format information. But to my knowledge no such tag exists, so I took this as an opportunity
to find out how to build my very own tag resolver :-)

Unfortunately I did not find a lot of information about this topic which meant I had find out the hard way, by looking at existing Wicket classes and the obvious choice was WicketMessageResolver, so what
you will see next is heavily inspired by this class even though I did change quite a few portions.

These are the basics that need to be done:

  • your class has to implement IComponentResolver
  • in a static initializer block make your tag well known by calling WicketTagIdentifier.registerWellKnownTagName(tagname)
  • in your application class register your resolver using getPageSettings().addComponentResolver(new YourResolver())
  • in the resolve method instantiate a subclass of MarkupContainer and add it to the resolvers container
  • make sure the subclass' isTransparentResolver() method always returns true, thus he can access it's parent's children as if they were it's own
  • and as a last you override the markup container's onComponentTag() method where you can put all the logic you need to handle your tag, create the rendered result and put it into the response object

And here my complete IncludeResolver, I hope this will be useful to someone someday. And as usual I would be greatful for comments, additions and corrections on this :-)

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.Response;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupElement;
import org.apache.wicket.markup.MarkupException;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.WicketTag;
import org.apache.wicket.markup.html.include.Include;
import org.apache.wicket.markup.parser.filter.WicketTagIdentifier;
import org.apache.wicket.markup.resolver.IComponentResolver;
import org.apache.wicket.model.Model;
import org.apache.wicket.response.StringResponse;
import org.apache.wicket.util.lang.PropertyResolver;
import org.apache.wicket.util.string.Strings;
import org.apache.wicket.util.string.interpolator.MapVariableInterpolator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class is a tag resolver to handle tags of the following format
 * <wicket:include [key="xxx"|filename="xxx"]/>.
 * 

* This class tries to provide the same functionality for included html files as * WicketMessageResolver does for property file entries but also * tries to avoid to fail early. *

* You have to specify either a key or a filename. A key will be looked up via * the resource mechanism and the result will be used as filename. *

* In addition you can nest tags within this tag. Those child tags will then be * used to generate the replacements for variables defined in the file content. * If this fails for a variable the parent's model object will queried for a * property with the variable's value as name. Should this also fail the last * fall back is the same query on the parent object. *

* If the resource settings are set to throw exceptions on missing properties * this class will raise an exception if either a variable cannot be looked up * or a child component is not replaced. *

* This tag allows you to easily include files in your pages which can also * depend on your localized properties. *

* Usage: *

* *

*     <wicket:include key="myFile">
 *        This text will be replaced with text from the specified file.
 *        <span wicket:id="replaceme">[to be replaced]</span>.
 *     </wicket:include>
 * 
* * Then your property file needs an entry like this: * *
* myFile = path / to / file
 * 
* * This file contains html fragments with variables: * *
* This is a file with ${replaceme}.
 * 
* * And your java component add: * *
* add(new Label("replaceme", new Model<String>("some replaced text")));
 * 
* * The output will be: * *
* This is a file with some replaced text.
 * 
* * @author Chris * */ public class IncludeResolver implements IComponentResolver { /** * */ private static final long serialVersionUID = -9164415653709941809L; private static final String tagname = "include"; private static final Logger log = LoggerFactory.getLogger(IncludeResolver.class); // register the tagname static { WicketTagIdentifier.registerWellKnownTagName(tagname); } /** * Handles resolving the wicket:include tag. If a filename is specified this * file will be looked up. If a key is specified it's value is looked up * using the resource mechanism. The looked up value will then be used as * filename. * */ public boolean resolve(MarkupContainer container, MarkupStream markupStream, ComponentTag tag) { if ((tag instanceof WicketTag) && tagname.equalsIgnoreCase(tag.getName())) { WicketTag wtag = (WicketTag) tag; String fileName = StringUtils.trimToNull(wtag.getAttribute("file")); String fileKey = StringUtils.trimToNull(wtag.getAttribute("key")); if (null != fileKey) { if (null != fileName) { throw new MarkupException( "Wrong format of : you must not use file and key attribtue at once"); } fileName = StringUtils.trimToNull(container.getString(fileKey)); if (null == fileName) { throw new MarkupException("The key inside could not be resolved"); } } else if (null == fileName) { throw new MarkupException( "Wrong format of : specify the file or key attribute"); } final String id = "_" + tagname + "_" + container.getPage().getAutoIndex(); IncludeContainer ic = new IncludeContainer(id, fileName, fileKey, markupStream); ic.setRenderBodyOnly(container.getApplication().getMarkupSettings().getStripWicketTags()); container.autoAdd(ic, markupStream); return true; } return false; } /** * Helper class to break open the original Include class by using * inheritance. * * @author Chris * */ private static final class ExposingInclude extends Include { /** * */ private static final long serialVersionUID = -212861726226482365L; public ExposingInclude(String id, String filename) { super(id, filename); } public final String expose() { return this.importAsString(); } } /** * Container class to render the included file. * * @author Chris * */ private static final class IncludeContainer extends MarkupContainer { /** * */ private static final long serialVersionUID = 3991477945186422264L; private final String key; private final String fileName; public IncludeContainer(String id, String fileName, String key, MarkupStream markupStream) { super(id, new Model(fileName)); this.key = key; this.fileName = fileName; } /** * Wrapper to log this instance's settings. * * @return String containing the instance's settings. */ private final String logParams() { return " tag with " + (null != this.key ? "key = '" + this.key + "' and resulting " : "") + "fileName = '" + this.fileName + "'"; } /** * Renders the component tag. The base content is read from the file * defined by the filename. Afterwards all containing variables are * replaced by the following methods in this order: *
    *
  • trying to get the value from a child component that has the
    * variable's value as id
  • *
  • trying to get the value as property from the parent's model
    * object
  • *
  • trying to get the value as property from the parent object
  • *
* * If all attempts to lookup up variable fail or not all child tags are * replaced either an exception is thrown or a warning is logged * depending on the resource settings. * */ @Override protected void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) { // get all child tags' rendered contents Map children = this.getRenderedChildren(markupStream, openTag); // read the included file content String fileContent = new ExposingInclude(this.getId() + "_internalInclude", this.getDefaultModelObjectAsString()).expose(); // remember all replaced child tags final Set replacedChildren = new HashSet(); // substitute all variables and set the result as response this.getResponse().write(new MapVariableInterpolator(fileContent, children) { @Override protected String getValue(String variableName) { // try to get value from rendered child tags String value = super.getValue(variableName); if (null != value) { // if we used a child tag create a marker replacedChildren.add(variableName); } else { if (log.isDebugEnabled()) { log.debug(IncludeContainer.this.logParams() + " - Could not find replacement value for variable = '" + variableName + "' in child tags"); } // see if the variable can be replaced using the // parent's model object - use try/catch to avoid fail // early try { value = Strings.toString(PropertyResolver.getValue(variableName, IncludeContainer.this.getParent().getDefaultModelObject())); } catch (WicketRuntimeException e) { if (log.isDebugEnabled()) { log.debug(IncludeContainer.this.logParams() + " - Could not find replacement value for variable = '" + variableName + "' in parent model"); } } } if (null == value) { // see if the variable can be replaced using the parent // object - use try/catch to avoid fail early try { value = Strings.toString(PropertyResolver.getValue(variableName, IncludeContainer.this.getParent())); } catch (WicketRuntimeException e) { if (log.isDebugEnabled()) { log.debug(IncludeContainer.this.logParams() + " - Could not find replacement value for variable = '" + variableName + "' in parent object"); } } } if (null == value) { // Handle failed lookup String logMsg = IncludeContainer.this.logParams() + " - Bailing with exception since variable = '" + variableName + "' could not be replaced"; if (IncludeContainer.this.isThrowingExceptions()) { if (log.isDebugEnabled()) { log.debug(logMsg); } markupStream.throwMarkupException(IncludeContainer.this.logParams() + " - Could not replace variable = '" + variableName + "'"); } else { log.warn(logMsg); } } return value; } }.toString()); // Make sure all of the children were rendered Iterator iter = children.keySet().iterator(); while (iter.hasNext()) { String id = iter.next(); if (replacedChildren.contains(id) == false) { String msg = "The for file " + this.fileName + "has a child element with wicket:id=\"" + id + "\". You must add the variable ${" + id + "} to the file content for the wicket:include."; if (this.isThrowingExceptions() == true) { markupStream.throwMarkupException(msg); } else { log.warn(msg); } } } } /** * Wraps call to resource settings to determine if a exception should be * thrown in case not all variables are resolved or not all children are * rendered. * * @return * Application.get().getResourceSettings().getThrowExceptionOnMissingResource() */ private final boolean isThrowingExceptions() { return Application.get().getResourceSettings().getThrowExceptionOnMissingResource(); } /** * Walks through all children of the current tag and stores their * rendering results in a map. * * @param markupStream * The markupStream associated with the tag. * @param openTag * The current include tag to be rendered. * @return Map containing all children's rendered responses associated * with each child's id. */ private Map getRenderedChildren(final MarkupStream markupStream, final ComponentTag openTag) { Map children = new HashMap(); if (!openTag.isOpenClose()) { while (markupStream.hasMore() && !markupStream.get().closes(openTag)) { MarkupElement element = markupStream.get(); // If it a tag like or if ((element instanceof ComponentTag) && !markupStream.atCloseTag()) { String id = ((ComponentTag) element).getId(); Component comp = this.getParent().get(id); if (comp != null) { children.put(id, this.getRenderedResponseString(markupStream, comp)); } else { markupStream.next(); } } else { markupStream.next(); } } } return children; } /** * Obtains the rendered response of the given component rendered * according to the markupStream. Rendering uses a temporary response * and the original response is restored before returning the result. * * @param markupStream * The markupStream used to render the component. * @param comp * The component to be rendered. * @return The rendered result. */ private String getRenderedResponseString(final MarkupStream markupStream, Component comp) { Response webResponse = comp.getResponse(); StringResponse response = new StringResponse(); try { this.getRequestCycle().setResponse(response); comp.render(markupStream); } finally { this.getRequestCycle().setResponse(webResponse); } return response.getBuffer().toString(); } /** * * @see org.apache.wicket.MarkupContainer#isTransparentResolver() */ @Override public boolean isTransparentResolver() { return true; } } }

Mittwoch, 3. August 2011

Firefox Plugin Pitfall

Today I did learn something about firefox plugins. We had some problems with a plugin provided by a 3rd party vendor that we integrated in our site.

The plugin did not install properly in firefox instance of version 4 or later. The respective extension got listed in the extension manager but the plugin itself did not show up in the list of installed plugins. This meant the plugin was not available and our site still asked for it to be installed. In firefox 3.6 everything was okay.

After a lot of research and some peeking around I finally found the solution. From firefox 4 onwards an extension has to declare if it needs to be unpacked by firefox to work or not. In versions older than 4 every extension was unpacked but in newer versions unpacking requires the presence of the unpack tag in the install.rdf packaged with every xpi file.

As described at the mozilla documentation the install.rdf needs to contain this entry:<em:unpack>true</em:unpack>

When I unzipped the xpi file in question and extracted the contained install.rdf I saw that it did contain the unpack tag but with a subtle differece: <em:unpack>True</em:unpack>

As you see the only difference is the uppercase "T". It was a long shot but since I had nothing to lose I changed the entry to "true" and repackaged the xpi. After putting up this new version to our website the plugin installed perfectly in every firefox version!!

Two things here really threw me off, first how our vendor could ship this version as this file cannot have every worked for firefox 4 for which it was explicitly built (but I will get back at them about this ;-)). And secondly why firefox does not recognize the entry just because of the case.

Anyways I had a hard time figuring this one out today so maybe this can be of use to someone :-)

Sonntag, 31. Juli 2011

Gnocchi-Gemüse-Pfanne

So, heute mal ein ganz anderes Thema, mein selbstkreiertes Rezept für eine leckere Gnocchi-Gemüse-Pfanne in Käsesauce :-)

Zutaten:

Die Mengenangaben orientieren sich an den erhältlichen Portionen meines örtlichen Supermarkts und können nach Belieben angepasst werden. Die hier angegebenen Mengen ergeben 4-6 Portionen.

1/2 Blumenkohl
1/2 Brokkoli
1 Bund Suppengrün (3-4 Karotten, etwas Selerie und Lauch sowie Petersilie)
1 Packung Schmelzkäse
200 ml Sahne
250 ml Milch
600g Schnitzelfleich
500g Gnocchi
Salz
Pfeffer
Gemüsebrühe
Muskat

Zubereitung
  • Blumenkohl, Brokkoli, Suppengrün und Fleisch mundgerecht schneiden
  • Blumenkohl und Brokkoli ca. 10 Minuten bissfest kochen
  • Suppengrün (ohne Petersilie) in eine große Pfanne mit heißem Öl geben und bei starker Hitze etwas anbraten
  • Das Fleisch in die Pfanne geben und bei mittlerer Hitze mitbraten, dabei öfters gut durchrühren
  • Wenn alles gut angebraten ist mit Sahne und Milch ablöschen gut durchrühren und Hitze wieder hochdrehen
  • Wenn die Sauce am köcheln ist den Schmelzkäse darin unter kräftigem Rühren auflösen
  • Inzwischen sollten Blumenkohl und Brokkoli fertig sein, also abgiesen und neues Wasser aufsetzen
  • Blumenkohl und Brokkoli in die Sauce geben und mit Salz, Pfeffer, Brühe, Muskat und Petersilie abschmecken
  • Sobald das Wasser kocht Salz und die Gnocchi hinzugeben
  • Wenn die Gnocchi fertig sind, wieder abgiesen und danach auch in die Pfanne geben
  • Nun alles weiterköcheln lassen und umrühren bis die Sauce die gewünschte Konsistenz hat
Wie üblich würde ich mich über Feedback oder Verbesserungsvorschläge freuen

Ansonsten, Guten Appetit!! :-)

Mittwoch, 13. Juli 2011

Validating Nested Forms in Wicket

Wicket provides a wide range of functionality and automatisms for creating and processing forms and thus makes handling user input very easy at least in most cases. When it comes to nested forms though some things might need some tweaking.

First of I want to say that the wicket documentation provides some very useful information about Nested Forms and Conditional Validation which covers this topic pretty much. But unfortunately one case is not really addressed there which I would like mention here.

I stumbled across this when I created two forms nested within each other where the inner form manipulated the content of the outer one and the outer form submits it's data to the backend while ignoring the inner form. So far no problem, but after adding some validators to the inner form submitting the outer form failed. As can be seen on the page for Nested Forms this is the desired default behavior, a form validates all it's children and thus also it's nested forms. So I had a look at Conditional Validation to see what the recommended way was to change this.

At first I tried to use IFormVisitorParticipant interface to isolate my two forms since this seemed the cleanest solution as the inner form would decide for itself if it wants to be validated or not in the given context. But it did not work for me. The problem was that the inner form did not only have validators assigned to it's components but also to itself and these form level validators were still executed.

So I had to look further. The Form class has a validate method that would be perfect for this purpose if it could be overwritten but for some reason (and I am sure it is a good one) this method is final. Another method that is called during validation of a component hierarchy is the isEnabled method but touching it has a lot of impact on basically all phases of a compoents life cycle and you cannot determine if the method is called during validation or another phase like rendering. And this might cause your component to be disabled or worse.

A suggestion from the wicket user mailing list was to use onFormSubmitted on the outer form to remove the validators from the inner form before submitting it and then adding them again. I did not like this idea since in my opinion it clearly breaks cohesion and the law of demeter.

So I decided to disable default form processing on the outer form and perform the validation of it's child components manually. But this also has the implication that the child components inputs are not put into the forms object model by the framework so you have to do this step yourself too.

All in all this is just minor anoyance but it would be pretty cool if IFormVisitorParticipant could be extended in a later release to completely disable a forms validation without affecting other life cycle phases. But I am not sure yet if this is at all possible. So for now we have to live with this ;-)

Samstag, 9. Juli 2011

Self Logging Exception Pattern

Error handling is an important and vital part of any serious application and usually a fair amount of application code addresses this topic. This is too some extend due to the fact that proper error handling requires to consider two aspects.

When ever an error occurs usually the control flow gets iterrupted and needs to take a different route. In Java this is done by throwing, catching, rethrowing and wrapping exceptions so that the application either aborts the current request to show an error message to the user or to recover from the error automatically.

Additionally those errors need to be logged to allow the support staff to see what is going on in the application and to be able to tell customers why their actions did not give the expected result and most importantly to find bugs.

This often leads to code blocks like the following:

UserTransaction transaction = null;
EntityManager em = null;
try {
  transaction = (UserTransaction) new 
      InitialContext().lookup("java:comp/UserTransaction");
  transaction.begin();
  em = emf.createEntityManager();
  em.merge(this.be);
  transaction.commit();
} catch (IllegalStateException ex) {
  logger.error("Persist failed", ex);
  throw new ApplicationException("Persist failed", ex);
} catch (RollbackException ex) {
  logger.error("Persist failed", ex);
  throw new ApplicationException("Persist failed", ex);
} catch (IllegalArgumentException ex) {
  logger.error("Persist failed", ex);
  throw new ApplicationException("Persist failed", ex);
} catch (TransactionRequiredException ex) {
  logger.error("Persist failed", ex);
  throw new ApplicationException("Persist failed", ex);
} catch (PersistenceException ex) {
  logger.error("Persist failed", ex);
  throw new ApplicationException("Persist failed", ex);
} catch (NamingException ex) {
  logger.error("Persist failed", ex);
  throw new ApplicationException("Persist failed", ex);
} catch (NotSupportedException ex) {
  logger.error("Persist failed", ex);
  throw new ApplicationException("Persist failed", ex);
} catch (SystemException ex) {
  logger.error("Persist failed", ex);
  throw new ApplicationException("Persist failed", ex);
} catch (SecurityException ex) {
  logger.error("Persist failed", ex);
  throw new ApplicationException("Persist failed", ex);
} catch (javax.transaction.RollbackException ex) {
  logger.error("Persist failed", ex);
  throw new ApplicationException("Persist failed", ex);
} catch (HeuristicMixedException ex) {
  logger.error("Persist failed", ex);
  throw new ApplicationException("Persist failed", ex);
} catch (HeuristicRollbackException ex) {
  logger.error("Persist failed", ex);
  throw new ApplicationException("Persist failed", ex);
} finally {
  em.close();
}

Quite anoying, isn't it? Now there are certain possibilities to try to reduce this boiler plate. The first attempt that is still employed far too often is to just catch Exception. This pattern is higly discouraged because it swallows exceptions resulting from coding errors like the very common NullPointerException. Another way to make the code a little bit cleaner is to wrap the log statement and exception generation into a convinience method that might also be parameterized with a message to be used as log ouput and the exceptons detail message:

private void bail(String msg, Throwable ex){
  logger.error(msg, ex);
  throw new ApplicationException(msg, ex);
}

...
} catch (SystemException ex) {
  this.bail("Error message 1", ex);
} catch (SecurityException ex) {
  this.bail("Error message 2", ex);
} catch (javax.transaction.RollbackException ex) {
  this.bail("Error message 1", ex);
} catch (HeuristicMixedException ex) {
  this.bail("Error message 1", ex);
...

But what if you do not only want to control the exception text to be displayed but also the kind of exception to be thrown. One could swap the error message for an enum and then decided within the bail method which exception should be thrown or to avoid this additional boiler plate add the exception class to the enum and then generate the exception by using reflection.

But all these approaches still rely on the programmer to make sure that where ever he handles the original excpetion he has to do both, log the error and throw the exception. Even if this is wrapped in a utility class that might be used throughout the application. Don't get me wrong, this approach is usually pretty fine and works rather well, at least as long as all developers involved in the project are aware of this necessity and can be trusted not to forget it. But often enough even the most reliable developers are in a hurry or the project staff changes or...

So I thought it would be nice to have mechanism that makes sure that when ever an application sepcific exception is thrown the corresponding error will be logged. That is why I came up with this idea:

public class ApplicationException extends RuntimeException {

  private static final long serialVersionUID = -4599465800805791368L;
  private static final Logger logger = Logger.getLogger(ApplicationException.class);

  private MessageCode code;

  private List<Object> params;

  public ApplicationException (MessageCode code) {
    this(code, null, null);
  }

  public ApplicationException (MessageCode code, List<Object> params) {
    this(code, null, params);
  }

  public ApplicationException (MessageCode code, Throwable cause) {
    this(code, cause, null);
  }

  public ApplicationException (MessageCode code, Throwable cause, List<Object> params) {
    super(getResolvedMessage(code.getMsg(), params), cause);
    this.code = code;
    this.params = params;
    this.log();
  }

  private void log() {
    StackTraceElement ste = this.getStackTrace()[0];
    if (logger.isEnabledFor(this.code.getLevel())) {
      logger.log(this.code.getLevel(), "ApplicationException thrown at "
        + ste.getClassName()
        + "."
        + ste.getMethodName()
        + ":"
        + ste.getLineNumber()
        + " with message '"
        + getResolvedMessage(this.code.getMsg(),this.params) 
        + "'");
    }
  }

  public String getKey() {
    return this.code.getKey();
  }

  private static String getResolvedMessage(String msg, List<Object> params) {
    if (null != params) {
      for (int i = 0; i < params.size(); i++) {
        if (null != params.get(i)) {
          msg = msg.replaceAll("\\{" + i + "\\}", params.get(i).toString());
        }
      }
    }
    return msg;
  }
}


public enum MessageCode {

  // General
  CODING_ERROR("Error.CodingError", "This is very likely a bug"),

  // Persistence
  PERSISTANCE_SAVE_FAILED("Error.Persistance.SaveFailed", "Error during entity persitance."),

  // User
  USERNAME_ALREADY_TAKEN("Info.User.NameAlreadyTaken", "Username '{0}' already taken", Level.INFO), 
  INVALID_PASSWORD("Info.User.LoginFailed", "Login failed due to an invalid password for user '{0}'", Level.INFO);

  private String msg;

  private String key;

  private Level level;

  private MessageCode(String key, String msg) {
    this(key, msg, Level.ERROR);
  }

  private MessageCode(String key, String msg, Level level) {
    this.key = key;
    this.msg = msg;
    this.level = level;
  }

  public String getKey() {
    return this.key;
  }

  public Level getLevel() {
    return this.level;
  }

  public String getMsg() {
    return this.msg;
  }
}


Here the MessageCode class takes several arguments, a key that defines the property key to be used when displaying an error message to the user, a string containing information for developers that will be used as log statement and an optional level defining the log level for the message. The latter is useful since not everything that causes request abortion is an error like a user providing an invalid password.

The exception class wraps that MessageCode and creates a log statement within it's constructor. Of course this has a draw back, most logging frameworks like e.g. log4j allow to automatically augment the log message with the class name and line number where the message was logged. With the log statement being created within the exception that piece of informaton is lost. To get around this limitation I am using the exeptions own stacktrace by accessing it's first element that contains the class name and line number where the exception constructor was invoked.

That way it is possible to log an error and abort request execution with one statement in your application code and by incooperating this capability into the exception class itself you can create your application specific exception hierarchy that inherits this behavior.

So far I am only using this pattern in my private projects but I am thinking of also introducing it to my customers' projects.

As always I would be very interessted in what you guys think of this approach especially if you see any problems with it or have suggestions for improvements.

Donnerstag, 7. Juli 2011

"Breaking Wicket ajax with broken markup" or "How to shoot yourself in the foot - Part 1"

Another day, another screw up ;-)

Here a short example how you can break Wicket ajax pretty easy but spend a lot of time on figuring out what went wrong.

I had a small page with a form in it and button issuing an ajax request. When clicking the button elements got added to the form using a PropertyListView. Not a hard task so it was set up pretty quick and did work like a charm. But then I thought "Would be nice to reuse this form on a few other pages as well." and decided to wrap it into a panel (panels are cool since they allow you to encapsulate the grouped components as well as their markup and properties).

Building the panel was also done real quick and after embedding it into my page instead of the original form I fired up my application again to see how well I had done and gloat a bit in front of.. we.. basically myself ;-)

I hit my nifty ajax button and BAMM!!! - FULL PAGE RELOAD!!!

After a short episode of wtfs and other cussing I tried to find out what the problem was. Conviniently the server logs showed exactly.. nothing. No errors, no warnings. Debugging the ajax call also did not work because the debugger did not get into any breakpoint, which showed that the ajax code did not get executed anymore.

Next I tried to check my changes but of course everything looked fine... (oh how wrong I was..)

So grabbed my last straw, the ajax debug window - savior of ajax screw ups all around the world. I opened it and started to fill the form fields, where each click got shown in the debug window but as soon as I hit the former ajax button the windows closed down - D'OH!

After trying it a few times I was able to spot an error message being displayed in the debug window before it closes. But unfortunately I could not see what the exact message was. So again I went double checking the java code and markup and suddenly my mistake revealed itself to me.

When I exchanged the form for the new panel in my page I forgot to change the markup tag the component was mapped to. And because the tag was associated to a form it was a form tag in the original version. But that form tag was now moved inside the panel and by forgetting to replace the page's form tag with a span tag the final markup ended up with a form wrapped inside a form.

After makeing the tag a span everything was working fine again. Having nested form tags is of course totally beyond any specifiaction etc. but I was a bit surprised that in effect this made my ajax request to behave like a regular page reload without any exceptions or anything like that.

And yes, this was somewhat embarrasing, but maybe someone else screws up the same way and this post helps him (or her) to save some time.. or just gives someone a good laugh *g*

Dienstag, 5. Juli 2011

Some Of My Custom Validators for Wicket

Currently I am having a closer look at the Wicket web framework which has some very nice aspects. One of it is it's validation support.There are plenty of tutorials and howtos on the net about how to build your own validators and how to configure them etc. So it would be mute to write yet another one of those (if you think differently let me know, but I doubt that pretty much ;-)).

Wicket has quite a few built in validators for a decent range of every day tasks that you need in most web applications. Those include length and range validations as well as equality of input fields (very useful for password handling) and much more. But I came across some cases that I found no suitable validators readily available. So I built a few of my own validators and would like to share the implementation of two of them because I think those could be useful for other people too.

The first validator is useful for something like a form to change a users password. In this case you want the user to enter a different password than his existing one. Wicket provides you with the EqualPasswordInputValidator to check if two password fields contain the same text but not for one to check the opposite, so here is my attempt for this task.

package org.butterbrot.rankem.validation;

import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.form.validation.AbstractFormValidator;
import org.apache.wicket.util.lang.Objects;

/**
 * Checks for two text components to contain different values.
 * 
 * 
 * @author Chris
 * 
 */
public class UnequalPasswordsValidator extends AbstractFormValidator {

 /**
  * 
  */
 private static final long serialVersionUID = 5677456180685647137L;
 private final FormComponent[] components;

 @SuppressWarnings("unchecked")
 public UnequalPasswordsValidator(FormComponent formComponent1, 
                        FormComponent formComponent2) {
  if (formComponent1 == null) {
   throw new IllegalArgumentException("argument formComponent1 cannot be null");
  }
  if (formComponent2 == null) {
   throw new IllegalArgumentException("argument formComponent2 cannot be null");
  }
  this.components = new FormComponent[] { formComponent1, formComponent2 };
 }

 public FormComponent[] getDependentFormComponents() {
  return this.components;
 }

 public void validate(Form form) {
  final FormComponent formComponent1 = this.components[0];
  final FormComponent formComponent2 = this.components[1];

  if (Objects.equal(formComponent1.getInput(), formComponent2.getInput())) {
   this.error(formComponent2);
  }

 }

}

Another one covers a more specialized use case. Assume you have a form where the user has to enter some numeric values like defining an interval or some kind of slice (maybe a portion of a video or a range) and you want to make sure that two values have a certain difference in their values. This validator allows you to check exactly that.

package org.butterbrot.rankem.validation;

import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.form.validation.AbstractFormValidator;

/**
 * Checks if the contents of two number components differ at least the specified
 * difference. If the difference has a positive value the first component must
 * contain the lesser value and vice versa. Since the check internally operates
 * on the double representations of the given values this class should not be
 * used for BigDecimal components.
 * 
 * @author Chris
 * 
 */
public class NumberDifferenceValidator extends AbstractFormValidator {

 /**
  * 
  */
 private static final long serialVersionUID = -1237357103535636603L;
 private final FormComponent[] components;
 private final Number difference;

 @SuppressWarnings("unchecked")
 public NumberDifferenceValidator(FormComponent formComponent1,
   FormComponent formComponent2, Number difference) {
  if (formComponent1 == null) {
   throw new IllegalArgumentException("argument formComponent1 cannot be null");
  }
  if (formComponent2 == null) {
   throw new IllegalArgumentException("argument formComponent2 cannot be null");
  }
  if (difference == null) {
   throw new IllegalArgumentException("argument dfference cannot be null");
  }
  this.difference = difference;
  this.components = new FormComponent[] { formComponent1, formComponent2 };
 }

 public FormComponent[] getDependentFormComponents() {
  return this.components;
 }

 public void validate(Form arg0) {
  if (this.components[1].getConvertedInput().doubleValue() - 
            this.components[0].getConvertedInput().doubleValue() < this.difference
    .doubleValue()) {
   this.error(this.components[0]);
  }

 }

}


Of course I would be grateful for any comments on what you think of these examples or how these could be improved.

Sonntag, 26. Juni 2011

B7 2011

And yet again I am coming home from a great Blood Bowl tournament, the famous B7 near Stuttgart/Germany. As usual the organization was great and the schedule was adhered to pretty much. Plus, as always, there were over 30 stadiums you could play in.

We arrived on Friday afternoon after a smooth ride and did enjoy a cheap and tasty barbeque as well as a couple of board games. Apart from my 6 games of Blood Bowl I also got to see some old buddies again some of which I had not seen for a few years. But now for the tournament itself.

Ruleset:
  • TV 1.100.000 GP
  • CRP + Experimental Races
  • 1 Skill before each game
  • Resurrection
  • No skill stacking
  • Only regular skills
  • Scoring system 4/2/1/0
My team (Dark Elf):
  • 2 x Witch Elf
  • 3 x Blitzer
  • 1 x Runner
  • 5 x Lineman
  • 3 x Reroll
Game 1: Prince Moranion (Dark Elf)

When I saw the initial draw I was pretty happy since this promised to be a great start for this tournament. We played a few times already and this guy is not only a veteran and pro player but also a very funny opponent to play against. We played a few times already and the games are usually tight with good positional plays plus a lot of trash talking from both sides. His setup was a bit different from mine, instead of the third reroll he upgraded one lineman to a blitzer and a second one to an assasin. I took leader on my runner while he took block on one of the witch elfs.
I got the first offence and started to pulk forward on the right wing but somehow I just got no real leverage to crack open the defence. Instead the opposition got to my runner with the ball and while the dump off failed the runner went down. Luckily the blitzing witch elf went down while trying to get to the ball. So I had to to go for a rather desperate move and put my witch elf into the end zone and managed to pass the ball to her. So i went 1-0 up but with still a few turns left in this half.
With that much time left and being a few people down I was not able to stop the equalizer so it was 1-1 at half time.
In the second half I was pretty much with my back to the wall while the Prince's players pounded my defence into the ground and set up a comfortable stalling cage with their complete team just before my endzone and the blockless witch elf holding the ball. I went in for the kill and put up as much pressure as I could but still my opponent refused to score after shaking of my pinning attempts. With not much else to do I sent in the kamikaze blitzer, 3+ 4+ 1 dice block on the blockless witch elf -> POW!!! Knockdown into the end zone, ball scatters out, throw in to the sideline, next throw in only 4 squares towards the LoS and the ball bounces into the hands of another blitzer that was pinned by an opposing player. He dodge out and ran for the hills while the few players left from my team tries to cover him as good as possible. My opponent only managed to tag the ball carrier with his assasin but I was able to get a lineman next to him so my blitzer could smack the assasin while moving towards the endzone and completely out of reach. Final result 2-1.

All in all this was great game where I had the lucky break in the end and feeling a little sorry for my opponent who played a great game and once again proofed to be a great sport even when being robbed in the last turns.

Game 2: Orkboss (Dark Elf)

Same team as before with the same line up, I took block on my first witch elf giving me the same skill set as my opponent. This match is summed up pretty quick, I had a good run of my block and cas dice killing his two skilled players within 4 turns. In the middle of the second half my opponent pretty much stopped trying to defend or anything due to a huge frustration wave caused by lack of players. I definitly had the better dice by far in this game but truth to be told my opponent gave me way too many blocks especially in the first turns even on his AV7 guys allowing me to take positional advantage and thus increasing pressure. So in the end I won 5-0.

Game 3: Flix (Dark Elf)

Again Dark Elves, again with the same skill selection as me, leader and 2 times block on the witch elves. The line up was similar to the opponents before but instead of an assasin he used a lineman and 2 fan factor. Flix and I have a pretty long history in Blood Bowl, mainly from online games, where he usually kicks my ass due to his excellent coaching skills but also his ability to pull out the crazy dice when needed, so I knew this one will get ugly but also with a high trash talk ratio. This time the dice where on my side, I caused 4 casualties where he did almost no damage to my team. In the middle of the second half his luck came back for a while and he managed to know the ball loose two times with a lot of dodges and 1 dice blocks. But fortunately I was always able to recover it and so I got my third win that day with another 2-1.

Game 4: Sputnik (Wood Elf)

Finally not Dark Elves, YAY!! Sputnik and I had not played for quite some time but again I knew this would a hard one and still more trash talk. Even though he rarely plays nowadays he is still a very good coach using a very good team, so I had to be careful. He got 2 war dancers, 2 catcher, 1 thrower and 7 linemen plus 2 rerolls. As skills he had leader on the thrower, frenzy and strip ball on the wardancers and block on a catcher, where i took dodge on a bltzer. I got the ball and started to grind forward but at some point the wardancers came to close for my taste so I chickened out and scored, giving him 4 turns for the equalizer. I think this was a mistake but hindsight is 20/20 as you know. Even though I got a Blitz! I could not put enough pressure on him to keep him from scoring. In the second half he first kept his thrower with the ball deep in his back field while catchers and wardancer terrorized my half of the pitch. After I sent a player after his thrower he went forward and put the ball into a 3 man side line cage. Since I was down in numbers I saw my only chance to try an early break. So I pinned the corner guards and had a blitzer dodge into the cage on a 4+ knocking down the thrower but unfortunatly catching the bounce while being trapped. So in the next turn he went out and the ball got thrown back to his endzone. After some more back and forth he was able to take advantage of his superior numbers and movement and sealed his 2-1 win.

Game 5: Hellraiser (Wood Elf)

Another Woodie and another trash talker that  had not played against in a  while. As usual he brought his tree, a thrower and 3 catchers but only 11 players in total. His skills were leader on the thrower, tackle on a wardancer and block on all catchers, while I went for another boring dodge blitzer. The kick of roll put me into defence but after a failed pass and some daft dodging I was able to steal the ball. From this point onwards the game went back and forth where the ball first almost got to my opponents endzone than back into the middle of my half and yet again back to my opponents side where i was finally able to score. This game was loaded with good dice rolls for me and some crucial turn overs on my opponent's side so I got to score a second time on my offence. But fighting hard the wood elves where finally able to score a touchdown elf style with some nice ball handling. Final result 2-1, once again.

Game 6: p4m (Skaven)

The race changed but the trash talking did stay. p4m and I only played once on tabletop at the B7 2007 but had quite a few online games and as far as I remember I usually lose against him. He had a rat ogre, 2 vermins, 4 gutter runners, 1 thrower and 12 players in total with 2 rerolls. As skills he chose juggernaut for the big guy, guard for the vermins as well as 2 times block and once wrestle on gutter runners and again I took dodge on a blitzer. The game started ugly, with a crowd push due to quick snap combined with an aggressive rat offence plus a maybe too passive defence on my side. After a while I had 2 linemen and a witch in my KO box while I put two gutter runners into my opponent's. But still I found no good way to the ball. So after a few turns I had to resort to desperate meassures once again. Another time a bliter made a 3+ 4+ dodge to POW the thrower with the ball and a lucky bounce gave me the shot for a pass. After the use of my reroll for a dodge the pass to my blitzer succeded who ran for his live crossing the LoS. The counter blitz against him resultet in a push and he was pinned by a vermin and a gutter runner. I was able to pin both so my blitzer could push the vermin with his blitz and run further to the endzone. Again a gutter runner tagged him but this time the vermin was not able to reach him by failing a go for it after makeing his 4+ and 3+ dodges. So I got a 2 dice blitz on the little rat and scored 1-0, UFF!!

But in the second half the tide turned yet again, I was down on players had a few turns that did not go that well plus a perfect defence to start with. So I lost the ball two times and both resulting in skaven touchdowns even though I tried to put up a fight and even got into the middle of the rats half. But in the end I just stood no chance and lost 1-2.

All in all I had a lot of high class games with some dice going my way and some not and this was again an excellent tournament that everyone should try to attend. My overall result was 4/0/2 putting me on the 10th place out of 70+ coaches which is kind of okay I guess for my first tournament with Dark Elves.

But now.. time for bed..

Donnerstag, 23. Juni 2011

Yet another way how Weblogic wasted my time..

A while ago I had some trouble with a Weblogic 10.0MP1 installation at my work place.I decided to post this here so that one day maybe it might help someone else facing the same problem.

At my workplace the operation guys provide a script to start a Weblogic instance which we have to use to get the instance configured properly.At one day I had to restart our test environment cluster due to an out of memory error. But for some reason the start script just hung without returning and the process list did not show a spawned java process, no errors, no log entries, nothing.

The start script was the same that was used on integration and production environments (hard link) which where still starting properly so the error could not have been there. To get some more information I started reading the script and added some debugging statements in a local copy of it. After a while I found the reason why the script hung. The java process just exited after being executed with a smooth exit code of 0. Meanwhile the script was listening to the log file in a loop waiting for either a success or an error message, since neither did get printed (no logs, see above) the readline call just blocked for I/O and would sit there until the end of days.

So now the question was why did the Weblogic server just shut down with a success return code and without any kind of error or log messages. Adding all kinds of debug flags to the start command did not change a thing. So it looked like a dead end, even more after searching the web for quite some time did not reveal any hits about this problem.

At this point I was pretty stumped and only saw one last chance to dig deeper into this, messing with the java code itself. I decompiled the Weblogic main class server.Weblogic and had a look at the code.At first I wanted to extend the class and override all methods to add debug statements and then delegate to the super class, but to my great joy the class is final. Using a delegate pattern was also of no use because as soon as I would delegate the first call to the original class the control would have never come back to me, so I decided to add plenty of printlns to the decompiled code, compiled it again and then used the new class to start the server.

Pretty soon I found the root cause of my problems: A bloody NullPointerException!!! Thrown by the classpath initialization routine. Now one would ask: "Why is a NPE not logged? Why did it set an error return code??" Weeeeeellll.. because the return value in that main routine is initially set to 0 and the whole block catches all Exceptions but only prints a statement if it was an AccessControlException. So for all other exceptions the routine just returns quitely and the return value is never changed. HOORAAYYY!!! Thank you Bea/Oracle.. ONCE MORE!! You nimrods!! Another stupid unnecessary bug caused by crap coding style every rookie java developer would be put against the wall for.

But this was not the end of it, I still had to figure out how that NPE got caused and more important how to fix it. The stacktrace did show that the Exception occured while the domain's lib folder was scanned. This folder can be used to put jar files there that should be added to the servers classpath which is rather convinient. When looking into the directory I saw the only jar we added there but also a directory containing the extracted contents of the jar file. After deleting the directory the server started up again.. UFFF!!

The directory got created by one of my colleagues who needed to know what the exact version of the jar file was and so had to look it up in the manifest. For some reason he was not able to deleted it afterwards, so he left it there to check back later. Can't really blame him for that, I would probably have done the same. Noone in our team would have suspected that this would cause such problems.

I spent the better part of a day fixing this problem, time that is now lost and that I have to make up for to keep our project on time. And so my hatred towards Weblogic raises yet again...

Ansteckende Dummheit

In meinem ersten Beitrag muss ich mir einfach etwas von der Seele schreiben was mir schon seit langer Zeit immer wieder die Sprache verschlägt.


Rolltreppen sind ja schon seit geraumer Zeit Teil des modernen Stadtbildes in Deutschland, besonders als Teil des öffentlichen Personennahverkehrs. Und auch die modernere Variante mit wechselnder Laufrichtung ist heutzutage auch keine Neuheit mehr. Von daher sollte man denken, dass Menschen die diese Treppen tagtäglich benutzen zumindest ein minimales Grundverständnis dafür haben wie die Dinger funktionieren!!

Der Vollständigkeit halber hier mal der grobe Ablauf:

  1. Die Rolltreppe steht still und es leuchtet an beiden Enden das Symbol für "Laufrichtung wechselt" (rotes Dreieck mit zwei gegenläufige Pfeilen)
  2. Eine Person tritt auf den Sensor am oberen Ende der Treppe
  3. Die Treppe beginnt zu laufen, von oben nach unten. Oben leuchtet nun zusätzlich das blaue Feld mit dem weißen Pfeil um zu zeigen, dass die Treppe von dieser Seite aus betreten werden kann. Unten leuchtet das Einbahnstraßensymbol um zu zeigen, dass man jetzt NICHT drauftreten sollte.
  4. Die Person verlässt die Treppe am unteren Ende wieder
  5. Nach wenigen Sekunden (vmtl. 2-3) hält die Treppe wieder an und es leuchten nur noch die Symbole für "wechselnde Laufrichtung"
Ist ja alles in allem nicht sooooo kompliziert, der Ablauf ist sehr simpel und es ist eigentlich klar, dass man ihn von außen nicht weiter beeinflussen kann. Tja, manche Leute sehen das offensichtlich anders. Zumindest ist das die einzige Erklärung die mir einfällt für das folgende Phänomen.

Immer häufiger beobachte ich wie Leute z.B. am unteren Ende einer Rolltreppe warten während diese gerade nach unten fährt und sobald die letzte Person von der Treppe gegangen ist, fängt einer der Wartenden an auf das blaue Pfeilsymbol zu drücken. Manchmal nur einmal, oft aber auch mehrmals hintereinander. Sobald dann die Treppe anhält schreiten diese Drücker dann auf die Treppe wobei sie in ihrem Gesicht einen Ausdruck der Zufriedenheit und Genugtuuung haben, als wollten sie sagen: "Ich habe diese Rolltreppe angehalten, ich ganz alleine!"

Und das sieht man immer öfter, immer wieder und man sieht dabei auch immer wieder die gleichen Leute. Andereseits habe ich auch schon beobachtet wie solche Drücker weiter hinten in der Warteschlange standen und ganz vorne so Leute wie ich, die einfach warten. Und es fällt den Drückern dabei offensichtlich überhaupt nicht auf, dass die Wartezeit ohne drücken genausolang ist als wenn sie sich den Finger an der Symbolleiste fast abbrechen.

Im Gegenteil, die Menge der Drücker wird immer mehr und wenn man es wagt an der Rolltreppe nicht zu drücken während die hinter einem stehen, dann kann man sich da ziemlich fiese Blicke einfangen.

Ich frage mich immer wieder was in den Köpfen von solchen Leuten vorgeht. Denken die, die Treppe würde immer weiter und weiter fahren wenn sie nicht auf den "Knopf" drücken? Und das die ganzen Male bei denen die Treppe so anhielt nur Zufälle waren? Glauben die Leute vielleicht auch, dass die Treppe einfach umdrehen würde wenn man nur oft genug auf den "Knopf" drückt während noch Leute drauf sind?

Das Ganze ist natürlich nichts dramatisches aber ein typisches Beispiel für die weiter fortschreitende und sich immer weiter ausbreitende Verdummung der Menschheit. Anstatt einmal richtig nachzudenken wird einfach irgendein Schwachsinn nachgemacht. Wenn Ihr das nicht glaubt, dann zählt mal alle paar Monate die Anzahl der Drücker die Ihr pro Woche seht und ich bin mir sicher es werden nicht weniger werden...