# Tomcat mit eigener Session-Implementierung



## jacquipre79 (11. Sep 2007)

Hallo,

Generell bin ich auch noch "neu" im Thema Servlets etc. und hoffe, dass ich mein Problem hier richtig darstellen kann und dass das auch hier in diesen Teil des Forums gehört.

Es ist so, dass wir für bestimmte Webapplikationen, die wir entwickeln, gern den Tomcat als ServletEngine/Webserver nutzen möchten. Das Problem ist aber das SessionHandling des Tomcats. Die Webanwendung, die wir benutzen, läuft im IE. Man meldet sich über Benutzernamen, Passwort und Domäne an (was quasi die Windows-Authentifizierung ist). In der Anwendung kann man bestimmte Items auswählen (z.B. Angebote), um diese weiter zu konfigurieren. Jedes Angebot wird in einem neuen Browserfenster geöffnet. Nutzen wir Tomcat als Server, dann werden alle Angebote, die ein Benutzer anklickt, unter derselben Session gespeichert, d.h. die SessionID bleibt immer dieselbe. Das ist aber für unseren Kontext schlecht. Es müsste so sein, dass für jedes Angebot eine neue Session erzeugt wird. Die Frage ist nun, wie man das realisieren könnte? Was wären mögliche Ansätze, um für den Tomcat ein eigenes SessionHandling zu implementieren. Geht das überhaupt? Wenn ja, wie? Kann man das auch irgendwie über Filter lösen? Indem man beispielsweise für Requests die Session verändert? Wenn ja, wie genau geht das? Hat jemand damit Erfahrungen gemacht oder weiß jemand, wo man sonst noch fragen könnte?

Viele Fragen. Ich hoffe, ihr könnt mir helfen.

Vielen Dank,
jacquipre.


----------



## ms (11. Sep 2007)

Warum ist das für euren Kontext schlecht?

ms


----------



## Kim Stebel (11. Sep 2007)

Ich hab da vor einiger Zeit mal einen ganz billigen Filter geschrieben, der eine neue Session vergibt, wenn sich die IP-Adresse ändert. Vielleicht kannst du den ja entsprechend modifizieren, so dass er bei bestimmten request-URLs eine neue Session anlegt.


```
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class IPFilter implements javax.servlet.Filter
{
  public void destroy()
  {

  }

  protected void initSession(HttpServletRequest request)
  {
    HttpSession session = request.getSession(true);
    String clientAddress = request.getRemoteAddr();
    String oldClientAddress = (String) session.getAttribute("clientAddress");
    if (oldClientAddress == null)
    {
      session.setAttribute("clientAddress", clientAddress);
    } else
    {
      if (!clientAddress.equals(oldClientAddress))
      {
        session.invalidate();
        session = request.getSession(true);
        session.setAttribute("clientAddress", clientAddress);

      }
    }
  }

  public void doFilter(ServletRequest request, ServletResponse response,
      FilterChain filterChain) throws IOException, ServletException
  {
    initSession((HttpServletRequest) request);
    filterChain.doFilter(request, response);
  }

  public void init(FilterConfig config) throws ServletException
  {

  }

}
```


----------



## jacquipre79 (13. Sep 2007)

Hallo, 

erstmal danke für die Antwort. 

Ich habe nochmal ein wenig hier recherchiert, um den Sachverhalt besser zu erklären:

Es ist so, dass wir uns mit einer Webanwendung in eine andere (von Microsoft) existierende Webanwendung integrieren. D.h. ein angemeldeter Benutzer kann im IE bestimmte Angebote sehen und bearbeiten und klickt er bei einem Angebot auf einen speziellen Link, dann gelangt er zu unserer Anwendung, mit der er dann diese konfigurieren kann. Normalerweise funktioniert unsere Anwendung immer mit Tomcat, eben nur im Microsoft-Fall nicht. Hier wird nämlich für einen Benutzer immer dieselbe Session benutzt. Öffnet man also ein Angebot, konfiguriert es, speichert es nicht und öffnet anschließend die Konfiguration eines anderen Angebots, dann gehen die Konfigurationsdaten des vorher geöffneten Angebots verloren, da wir immer noch in derselben Session sind. Es ist so, dass jede Konfiguration eine eigene Session benötigt.

Bisher war es so, dass wir einen eigenen ServletContainer geschrieben hatten - mit eigenem Session Handling. Eine neue Session wurde immer dann erzeugt, wenn ein neues Angebot konfiguriert wurde; und zwar mit Hilfe der Angebots-ID (die für jedes Angebot individuell ist). Das hat soweit funktioniert, nur wurden unsere Webanwendungen so komplex, dass stets der ServletContainer um Funktionalität erweitert werden mußte, dass sich letztlich die Frage stellte, nicht doch den Tomcat zu nutzen.

Wenn man denn dort irgendwie ein eigenes Session Handling einbauen kann? Den Filter habe ich mal probiert, aber irgendwie scheint das noch nicht so richtig zu funktionieren...

Ich habe auch gelesen, dass man für den Tomcat einen eigenen SessionManager implementieren kann und den für jeden Kontext sogar. Da wir nur eine Webanwendung in einem Kontext haben, wäre das vielleicht eine Alternative? Wenn ja, was müßte da wohl passieren?

Grüße,
jacquipre


----------



## Kim Stebel (13. Sep 2007)

> Wenn man denn dort irgendwie ein eigenes Session Handling einbauen kann? Den Filter habe ich mal probiert, aber irgendwie scheint das noch nicht so richtig zu funktionieren...


Und warum? Wo liegt das Problem?


> Öffnet man also ein Angebot, konfiguriert es, speichert es nicht und öffnet anschließend die Konfiguration eines anderen Angebots, dann gehen die Konfigurationsdaten des vorher geöffneten Angebots verloren, da wir immer noch in derselben Session sind. Es ist so, dass jede Konfiguration eine eigene Session benötigt.


Das scheint mir das eigentliche Problem zu sein. Man könnte durchaus Konfigurationsdaten mehrerer Angeboten in einer Session speichern, ohne dass sie sich gegenseitig stören.

Mit der Implementation eines eigenen Session-Handling kenne ich mich nicht aus. Aber gibt es in den Tomcat-Docs nichts dazu?? Aber das wäre in eurem Fall mit Kanonen auf Spatzen schießen...


----------



## Tactive (14. Sep 2007)

Hallo, 

ich würde dafür auch keine eigene Session-Implementierung schreiben, denn es ist möglich an der Session ein X-Beliebiges Objekt mit dem Befehl setAttribute() zu hinterlegen. Das kann dann auch eine Liste oder Map sein, in die Angebote abgelegt sind.


```
request.getSession().setAttribute("angebote", new HashMap<String, Angebot>());
```

das abrufen dieses Objekt geht natürlich dann wieder per getAttribute().


----------



## jacquipre79 (14. Sep 2007)

Also, ich habe das jetzt mal diesen Filter implementiert:

```
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
    HttpServletRequest wrequest =  (HttpServletRequest) request;
    HttpSession session = wrequest.getSession(true);
    String requestURI = wrequest.getRequestURI();
    String sessionID = session.getId(); // nur für Testausgaben

    // Ist in der RequestURI eine spezielle ID (die des Angebots) enthalten, dann muss eine neue
    // Session erzeugt werden, da neues Angebot
    if (requestURI.indexOf("myspecialid") != -1)
    {
         session.invalidate();
         session = wrequest.getSession(true);
         sessionID = session.getId(); // für Testausgaben
     }
     logClass.debug("Request URI: " + requestURI);
     logClass.debug("SessionID: " + sessionID);

     chain.doFilter(request, response);
}
```

Es wird jetzt also immer eine neue Session erzeugt, wenn ein neues Angebot geöffnet wird, also auch eine neue Angebots-ID ankommt. Das funktioniert im Moment anscheinend, aber nur, weil ich Cookies für diesen Kontext im Tomcat ausgeschaltet habe. Tu ich das nicht, dann steht im Request-Header immer noch als Cookie die JSESSIONID drin. Und diese bleibt auch so, wenn ich eine neue Session erzeuge, d.h. ich kann diesen Cookie-Wert nicht auf die neue SessionID ändern, weil ich für das Request-Objekt keine Methode finde, die ein Cookie setzt oder hinzufügt. 

Im Fall ohne Cookies finde ich auch komisch, dass für bestimmte Ressourcen, z.B. Javascript-Dateien trotzdem andere SessionIDs übertragen werden; das macht mich stutzig.

Beispiel (ein Angebot):

```
Request URI: /gui/css/ressourceA.css 
SessionID: 720F27EFBB1C346290F51968773F2E03   <--- die neue ID die ich vie session.invalidate() erzeugt habe.

Request URI: /gui/library/include/ressourceB.js 
SessionID: 81ED6878D1C97F5311C9731B6663A318 <---- eine andere ID, obwohl nicht invalidated wurde (???)
```

Woran kann das wohl liegen?




			
				Tactive hat gesagt.:
			
		

> das abrufen dieses Objekt geht natürlich dann wieder per getAttribute().



Ja, aber dieses Abrufen und Auswerten müßte ja dann im entsprechenden Servlet geschehen, oder? Den Servlet-Code kann ich aber nicht ändern, der sollte eigentlich schon so bleiben wie er ist, weil diese auch in anderen Projekten angewendet werden, wo wir dieses Session-Problem nicht haben. Oder?

Grüße,
jacquipre


----------



## ms (14. Sep 2007)

Kann man das als Portal sehen, wo mehrere Anwendungen in einer Webapplikation laufen?

ms


----------



## maki (14. Sep 2007)

Hallo jacquipre,

das Sessionhandling ist ziemlich gleich in allen Standardkonformen Servletcontainern, wenn keine Cookies verwendet werden, muss die SessionId in der URL mitgeschickt werden, wenn dann keine SessionId in der URL rüberkommt, ist es eben eine neue Session.
Cookies haben immer Vorrang vor URL Codierten sessionIds, da sie sicherer sind (Mit URL codierten sessions muss der entwickler die sessionid immer mitschicken bei links und ähnlichen, sonst wird eine neue Session erzeugt  )
Die Servlet Spezifikation ist da das richtige Nachschlagewerk.



> Den Servlet-Code kann ich aber nicht ändern, der sollte eigentlich schon so bleiben wie er ist, weil diese auch in anderen Projekten angewendet werden, wo wir dieses Session-Problem nicht haben. Oder?


Ehrlich gesagt solltest du das Sessionhandling in eurer Anwendung (Servlet) korrigieren, denn das ist die Ursache und das eigentliche Problem.

Kurz: Halte dich an den Standard, und Tomcat (und alle Standardkonformen anderen Servletcontainer ) ist dein Freund.

Falls du auf der Suche nach einem (dreckigen) Hack bist, solltest du bei URL codierten Session bleiben


----------



## jacquipre79 (17. Sep 2007)

maki hat gesagt.:
			
		

> das Sessionhandling ist ziemlich gleich in allen Standardkonformen Servletcontainern, wenn keine Cookies verwendet werden, muss die SessionId in der URL mitgeschickt werden, wenn dann keine SessionId in der URL rüberkommt, ist es eben eine neue Session.



Ok, das erklärt auch, warum ich dann immer eine neue Session erhalte. Es gibt für im Request-Header keinen "Cookie"-Eintrag und auch in der URL steht keine SessionID. Deshalb erzeugt der Tomcat eine neue Session. Richtig?



			
				maki hat gesagt.:
			
		

> Cookies haben immer Vorrang vor URL Codierten sessionIds, da sie sicherer sind (Mit URL codierten sessions muss der entwickler die sessionid immer mitschicken bei links und ähnlichen, sonst wird eine neue Session erzeugt  )
> Die Servlet Spezifikation ist da das richtige Nachschlagewerk.



Ja, verstanden 



			
				maki hat gesagt.:
			
		

> Ehrlich gesagt solltest du das Sessionhandling in eurer Anwendung (Servlet) korrigieren, denn das ist die Ursache und das eigentliche Problem.



Die Anwendung funktionierte bisher eben immer so, dass für einen Anwender alles innerhalb einer Session läuft. Die Anwendung müsste also irgendwie mitkriegen, welches Angebot gerade aktuell ist, z.b. über einen Request-Header ("angebotsid") und dies dann auswerten? Und was muss dann passieren?


----------



## maki (17. Sep 2007)

> Ok, das erklärt auch, warum ich dann immer eine neue Session erhalte. Es gibt für im Request-Header keinen "Cookie"-Eintrag und auch in der URL steht keine SessionID. Deshalb erzeugt der Tomcat eine neue Session. Richtig?


Ja, richtig.



> Die Anwendung funktionierte bisher eben immer so, dass für einen Anwender alles innerhalb einer Session läuft. ..


Das wäre der "Standard" weg, alles eben innerhalb einer Session, ich denke aber, das deine Anwendung jedesmal eine neue Session erzeugen soll, wenn ein Angebot geöffnet wird.

So würde ich da rangehen:
- Deaktiviere die Möglichkeit, dass Tomcat cookies  zur speicherung der Session nuzt (denke das war in der server.xml).
- Wenn du eine neue Session erzeugen möchtest, benutzte einen link, der kein jsessionid parameter hat.
- Möchtest du, dass keine neue Session erzeugt wird, musst du die Session Id in jedem (!!!) request zum server mitschicken, hier musst du darauf achten, das du keinen link/url übersiehst.

Die Session Id lässt sich mit Scriplets in die URL in JSPs einsetzen:

link
link text

action für ein Formular
<form action="meineResource;jsessionid=<%= session.getId() %>" method=POST">

Der grüne Teil ist der name des Parameters (in diesem Falle natürlich jsessionid), der rote Teil ist die session id an sich.

Alternativ köntest du natürlich auch die Anwednung so umschreiben, dass der Sessionscope nicht mehr verwendet wird, um Angebotdaten zu halten.

edit: Fehler korrigiert


----------



## jacquipre79 (17. Sep 2007)

maki hat gesagt.:
			
		

> So würde ich da rangehen:
> - Deaktiviere die Möglichkeit, dass Tomcat cookies  zur speicherung der Session nuzt (denke das war in der server.xml).
> - Wenn du eine neue Session erzeugen möchtest, benutzte einen link, der kein jsessionid parameter hat.
> - Möchtest du, dass keine neue Session erzeugt wird, musst du die Session Id in jedem (!!!) request zum server mitschicken, hier musst du darauf achten, das du keinen link/url übersiehst.



Gibt es sonst irgendeinen Nachteil, wenn ich beim Tomcat Cookies deaktiviere?




			
				maki hat gesagt.:
			
		

> Die Session Id lässt sich mit Scriplets in die URL in JSPs einsetzen:
> 
> link
> link text
> ...



Ok, das müsste ich dann natürlich direkt in unserer Anwendung ändern, richtig?


----------



## maki (17. Sep 2007)

> Gibt es sonst irgendeinen Nachteil, wenn ich beim Tomcat Cookies deaktiviere?


Naja, wenn die Session per URL parameter übermittelt wird, ist der Entwickler dafür verantwortlich. Im allgemeinen benutzt man immer die cookies, weil zuverlässiger 



> Ok, das müsste ich dann natürlich direkt in unserer Anwendung ändern, richtig?


Ja, in den JSPs.
Ehrlich gesagt sehe ich keinen Weg der daran vorbeiführt, aber vielleicht hat ja noch jemand anderes eine bessere Idee. 

Mein Vorschlag hier ist natürlich nur ein dreckiger Hack, weil dass eigentliche Problem (fehlerhafte Webanwendung) nicht angegriffen wird, dafür ist es aber eine relativ einfache und schnelle Möglichkeit, sollte sie denn funktionieren.


----------



## jacquipre79 (18. Sep 2007)

maki hat gesagt.:
			
		

> Naja, wenn die Session per URL parameter übermittelt wird, ist der Entwickler dafür verantwortlich. Im allgemeinen benutzt man immer die cookies, weil zuverlässiger



Aha. Das heißt aber nur, dass das Schreiben der SessionIDs über Cookies untersagt wird, wenn ich das im Tomcat austelle, oder? Generell kann unsere Anwendung ja immer noch mit Cookies arbeiten, oder habe ich da was missverstanden :?  ?



			
				maki hat gesagt.:
			
		

> Mein Vorschlag hier ist natürlich nur ein dreckiger Hack, weil dass eigentliche Problem (fehlerhafte Webanwendung) nicht angegriffen wird, dafür ist es aber eine relativ einfache und schnelle Möglichkeit, sollte sie denn funktionieren.



Ja. Das Problem liegt wirklich in dieser Anwendung. Nur leider habe ich da keinen Einfluss drauf    Ich soll halt herausfinden, ob es seitens Tomcat eine Lösung dafür gibt, da dieses Problem ja sonst nicht existent ist, sondern nur in diesem einen Fall. 

Was ich jetzt mal probiert habe, ist ein sogenanntes Valve (Ventil) für Tomcat zu schreiben, dass die Requests immer vorher abfängt und überprüft, ob in der RequestURI eine neue AngebotsID übergeben wurde. Ist diese AngebotsID noch nicht in meiner Liste vorhanden, dann füge ich sie hinzu. Außerdem werden alle RequestURI's um die jeweilige JSESSIONID erweitert. Hier mal der Code dazu:

```
private static final String JSESSIONID = "jsessionid";
private static final String QUOTEID = "quoteid";
private static String currentQuoteID;
private static HashMap quoteIDList = new HashMap();
...
public void invoke(Request request, Response response) throws IOException, ServletException 
{
    	String requestURI = request.getRequestURI();
    	String referer = request.getHeader("referer");
    	
    	if (requestURI.indexOf(QUOTEID) != -1)
    	{
                 if (!quoteIDList.containsKey(tempID))
		{
			quoteIDList.put(tempID, request.getSession().getId());
			currentQuoteID = tempID;
		}
    	}
    	
    	// get the current 'jsessionid' corresponding to the current 'gonzalesid'
    	// and add this to every url 
    	String sessionID = (String) quoteIDList.get(currentQuoteID);
    	
    	int semicolonMark = requestURI.indexOf(";");
    	int questionMark = requestURI.indexOf("?");
    	
    	
    	if (semicolonMark != -1)
    	{
                ...
    		// einige Stringoperationen, um QueryString etc. aus der Url zu extrahieren
                // und eine requestURI zu bauen, die 'jsessionid=[Wert von sessionID]' enthält
    	}
    	
        request.setRequestURI(requestURI);
    	
    	// call next valves (if existent)
    	getNext().invoke(request, response);
    		
}
```

Ich weiß nicht, ob das Sinn macht, ob das richtig ist, ob Probleme auftreten könnten? Was ich festgestellt habe ist, dass nun für jedes Angebot (also immer wenn eine neue quoteid übermittelt wird) eine neue Session erzeugt wird, da ja kein Cookie mit dem Eintrag JSESSIONID existiert und in der URL auch keine drin steht...

Habe ich da einen Denkfehler?

-- jacquipre

P.S.: Ich wollte mich hier mal für die Antworten und die Hilfe bedanken. Das freut mich sehr


----------



## jacquipre79 (18. Sep 2007)

Mir ist gerade aufgefallen, dass ich den ganzen "Quatsch" mit dem Neubauen der RequestURI gar nicht brauche. Auch das Setzen dieser per request.setRequestURI(...) scheint nichts zu bringen. Rufe ich sofort danach request.getRequestURI() ab, dann steht da immer noch die alte drin. 

Mit ein paar Logausgaben habe ich jetzt festgestellt, dass für jedes Angebot eine neue Session erzeugt wird. (Mit Hilfe eines SessionListeners finde ich heraus, wann eine Session erzeugt wird.) 

Die wichtigste Stelle ist hier zu finden:

```
if (requestURI.indexOf(QUOTEID) != -1)
{
      if (!quoteIDList.containsKey(tempID))
	   {
		... request.getSession() ...
	   }
}
```

Immer, wenn also eine neue AngebotsID übergeben wird, dann wird per request.getSession() anscheinend eine neue Session erzeugt, da weder ein Cookie JSESSIONID existiert noch eine jsessionid in der URL übergeben wurde und somit keine Session existiert, richtig?

Was passiert dann wohl, wenn ich erst Angebot A öffne, dann Angebot B und dann wieder Angebot A. Es müssten dann drei Sessions erzeugt werden, oder? Ob das gut ist...? Müßten die dann nicht irgendwie wieder zerstört werden?

-- jacquipre


----------



## maki (20. Sep 2007)

Mit Filtern/Valves kann ich dir nicht helfen.

Wenn du weisst, wann du eine neue Session erzeugen musst, kannst du das mit session.invalidate(), daraufhin sollte die Session ungültig gemacht werden und mit request.getSession( true ) sollte danach eine neue generiert werden, sollte... habs nicht ausprobiert.

Wichtig ist, das der Browser in diesem Moment keine session Cookie mehr hat, also Sessiontracking auf URL basis einstellen.


----------

