Autorenarchiv

Nicht mehr oder noch nicht angemeldet?

Montag, 30. März 2009

In Webanwendungen ist ein Teil der Funktionen häufig nur für angemeldete Benutzer verfügbar.
So kann es passieren, dass die Session verfällt, wenn der Benutzer zu lange nichts in der Anwendung getan hat. Da der Benutzer jedoch vielfach noch seinen Browser und auch die Anwendung geöffnet hat, werden wir häufig damit konfrontiert den Benutzer darüber zu informieren, dass seine Session nicht mehr gültig ist.
Der direkte Aufruf von URLs führt uns schliesslich zu der Frage: Bin ich nicht mehr oder noch nicht angemeldet?

Was wollen wir sagen?
Was sagen wir dem Benutzer, wenn er auf eine Seite zugreifen will, für die eine Anmeldung erforderlich ist? Hierfür kann es verschiedene Gründe geben:

  • die Session ist verfallen
  • der Benutzer folgt einem Deeplink oder hat sich ein Lesezeichen gesetzt

Für die Anwendung sehen beide Fälle fast identisch aus, jedoch ist es für eine gute Benutzerführung eine aussagekräftige Fehlermeldung erforderlich.

Entscheidungskriterien
Die Lösung hierfür liegt in zwei kleinen Funktionen, die wir uns durch Kombination zu Nutzen machen können:

  • getRequestedSessionId() in javax.servlet.http.HttpServletRequest
  • isNew() in javax.servlet.http.HttpSession

Eine Sitzung ist dann verfallen, wenn der Client eine Session-ID mitschickt, der Server dennoch eine neue Session erzeugt. In Java können wir den Zustand wie folgt prüfen:

if (request.getRequestedSessionId() != null 
    && request.getSession().isNew()) {
    // session invalidated
} else {
    // login required
}

Ausnahmen
Die oben beschriebene Lösung funktioniert gut wenn die Sessions mittels Cookies verwaltet werden. Probleme macht der Fall, wenn der Benutzer keine Cookies zulässt und ein URL-Rewriting genutzt werden muss. Hierbei wird die URL mit der Session-ID erweitert; der einzelne Link wird Session-abhängig. Lesezeichen einer solchen URL würden dann immer eine Session-ID mitsenden: eine falsche Fehlermeldung wäre vorprogrammiert.
Glücklicherweise dürfte dieser Fall eher selten vorkommen, da der Benutzer schon generell die Verwendung von Cookies ablehnen müsste.


Thomas Bader


Gleichheit von Entitäten in Hibernate

Donnerstag, 03. April 2008

Wann sind Entitäten gleich?

Dies ist eine Frage, die einen regelmäßig in Diskussionen mit anderen Entwicklern verfallen lässt. Es gibt hier zwei Theorien, wann eine Entität gleich ist:

  • Gleichheit der primären Schlüssel der Entitäten
  • Gleichheit aller Attribute der Entitäten

Das Verständnis von relationalen Datenbanken und Entitäten lässt aber den zweiten Ansatz sinnlos erscheinen: Dort ist eindeutig geregelt, das zwei Entitäten gleich sind, wenn ihre Primärschlüssel gleich sind. Alles andere würde die Auffindbarkeit von Einträgen in der Datenbank unmöglich machen.

Unter dieser Prämisse verfolgen wir also den ersten Ansatz!

Vergleich in Java

Der Vergleich in Java ist einfach zu bewerkstelligen und erfolgt durch das Überschreiben der equals()-Methode. Wichtig hierbei ist auch daran zu denken, die hashCode()-Methode zu überschreiben. Dies wird von der Java-Dokumentation gefordert und kann ansonsten leicht zu Problemen führen (sogenannter equals()-Contract).

Wichtig bei einer sauberen Implementierung ist auch der Vergleich von nicht gespeicherten mit gespeicherten Objekten, da nicht gespeicherte Objekte noch keine ID haben.
Dies ist im Zusammenhang mit Hibernate wichtig, da hier Relationen normalerweise mit java.util.Set abgebildet werden.
Würde man lediglich einen Vergleich über den Primärschlüssel machen, wäre es nicht möglich zwei neue Entitäten einer solchen Relation hinzuzufügen bevor man speichert, da das Set kein Objekt doppelt beinhalten kann und dies über die equals()-Methode prüft.

Leider gibt es zu diesem Thema auch seitens Hibernate keinen eindeutigen Tipp, der mich zufrieden stellen konnte.

Lösung

Aus den oben geführten Überlegungen und Recherchen, ergibt sich die folgende Implementierung der Methoden hashCode() und equals().

Implementierung der hashCode-Methode:

public int hashCode() {
   if (getId() != null) {
      return getId().hashCode();
   } else {
      return System.identityHashCode(this);
   }
}

Implementierung der equals-Methode:

public boolean equals(Object obj) {
   boolean isEqual = false;
   if (obj == null) {
      isEqual = false;
   } else if (this == obj) {
      isEqual = true;
   } else if (!this.getClass().equals(obj.getClass())) {
      isEqual = false;
   } else {
     VO castObj = (VO) obj;
 
      EqualsBuilder builder = new EqualsBuilder();
      builder.append(
         this.getId() != null 
            ? this.getId() 
            : "sid_"+ System.identityHashCode(this),
         castObj.getId() != null 
            ? castObj.getId() 
            : "sid_"+ System.identityHashCode(castObj));
      isEqual = builder.isEquals();
   }
 
   return isEqual;
}

(Für den Vergleich wird hier der EqualsBuilder aus dem Projekt commons lang von Apache verwendet.)

Das Prinzip hier ist denkbar einfach: Für den Fall, dass eine Entität keinen Primärschlüssel hat, wird einfach ein “künstlicher Primärschlüssel” mit dem ursprünglichen Hash des Objektes selbst gebildet.
Ein Blick in der Java-API auf System.identityHashCode() erleichtert das Verständnis.


Thomas Bader


Einrichtung des Blogs

Dienstag, 01. April 2008

So … es ist nun geschafft: Das Blog steht.

Und so sind wir nun ein wenig weiter bei dem Plan interessante Probleme und deren Lösungen aus dem Bereich der Software-Entwicklung zu beleuchten und die gefundenen Lösungsstrategien aufzuzeigen.

Und das ist wirklich ein weites Feld.


Thomas Bader