[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[rap-dev] RAP & Multitab

Hi

I want to describe the way that you can use to deal with multitab browsers
(or several frames on the html page with the same RAP application from one
server inside).

The way that I use is a wrapping the request and session objects. This
approach based on using special attribute (instanceId) that I we can pass
with http request object or with "referer" header.

I attached patch with, you can use it to see all my changes.

First of all - there are three main classes in the
org.eclipse.rwt.internal.service.wrapper package: RequestWrapper,
SessionWrapper and SessionManager.

1) About RequestWrapper:

It used to wrap request object in special object. There are several members
that overrides the default implementation: getParameter(String name),
getSession() and getSession(boolean isNew). All other methods delegate to
their implementation in wrapped request object. The getSession() and
getSession(boolean isNew) use special singleton class SessionManager to get
a session object.

I know there is a class org.eclipse.rwt.internal.service.WrappedRequest. And
I was able to use that class instead of mine, but I prefer not to change the
platform classes if it possible (until I will be sure that my fix is fine).

2) About SessionWrapper:

SessionWrapper wraps HttpSession object and uses those storage to save own
data. I use instanceId attribute to save information from different tabs (or
frames).

3) About SessionManager:
This class maps pairs of real HttpSession objects and instanceId strings to
SessionWrapper objects.

And there is some additional fixes:
In org.eclipse.rwt.internal.service.ServiceContext I changed the constructor
to use RequestWrapper instead of real (and wrapped) HttpServiceRequest.
In org.eclipse.rwt.internal.service.LifeCycleServiceHandler I changed the
isSessionRestart method (removed some conditions, that checks if the new
HttpSession object used).

You have to know that due to
org.eclipse.rwt.internal.service.LifeCycleServiceHandler#isSessionRestart
implementation you must use branded entry poins (use, for example,
tea-branding from org.elcipse.rap.branding). The problem of using the
"rap?startup=default" in the fact that the isSessionRestart method use a
startup parameter to check if the session should be restarted.


There is one more thing that you have to know: if you have to implement the
some service handler, you must pass instanceId variable with the (get)
parameters to this service handler. But the fact that I uses "referer"
header to look for the instanceId parameter could reduce the places, where
you have to pass instanceId attribute.


I made some additional changes in my application. For example, I used a
"org.eclipse.equinox.http.registry.resources" where I registered some
additional pages that used a javascript to calculate a new free instanceId
value and passed it to the iframe object. So the user can only type the name
of an application to open it. I can describe how to do this if it is
necessary.


Please, review my patch. Is this a good way to deal with multitab support?
And is it possible to patch RAP js-code to deal with calculation of
instanceId attribute?


Regards,
Igor
### Eclipse Workspace Patch 1.0
#P org.eclipse.rap.rwt
Index: src/org/eclipse/rwt/internal/service/LifeCycleServiceHandler.java
===================================================================
RCS file: /cvsroot/rt/org.eclipse.rap/runtime.rwt/org.eclipse.rap.rwt/src/org/eclipse/rwt/internal/service/LifeCycleServiceHandler.java,v
retrieving revision 1.12
diff -u -r1.12 LifeCycleServiceHandler.java
--- src/org/eclipse/rwt/internal/service/LifeCycleServiceHandler.java	17 Oct 2008 11:16:30 -0000	1.12
+++ src/org/eclipse/rwt/internal/service/LifeCycleServiceHandler.java	10 May 2009 14:25:01 -0000
@@ -161,9 +161,7 @@
     HttpServletRequest request = getRequest();
     boolean startup = request.getParameter( RequestParams.STARTUP ) != null;
     String uiRoot = request.getParameter( RequestParams.UIROOT );
-    HttpSession session = request.getSession();
-    return    !session.isNew() && !startup && uiRoot == null 
-           || startup && isBrowserDetected();
+    return !startup && uiRoot == null || startup && isBrowserDetected();
   }
   
   public static void initializeStateInfo() {
Index: src/org/eclipse/rwt/internal/service/ServiceContext.java
===================================================================
RCS file: /cvsroot/rt/org.eclipse.rap/runtime.rwt/org.eclipse.rap.rwt/src/org/eclipse/rwt/internal/service/ServiceContext.java,v
retrieving revision 1.3
diff -u -r1.3 ServiceContext.java
--- src/org/eclipse/rwt/internal/service/ServiceContext.java	12 Jun 2008 13:12:17 -0000	1.3
+++ src/org/eclipse/rwt/internal/service/ServiceContext.java	10 May 2009 14:25:01 -0000
@@ -13,6 +13,7 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.eclipse.rwt.internal.service.wrapper.RequestWrapper;
 import org.eclipse.rwt.internal.util.ParamCheck;
 import org.eclipse.rwt.service.ISessionStore;
 
@@ -52,7 +53,7 @@
   {
     ParamCheck.notNull( request, "request" );
     ParamCheck.notNull( response, "response" );
-    this.request = request;
+    this.request = new RequestWrapper(request);
     this.response = response;
   }
 
Index: src/org/eclipse/rwt/internal/service/wrapper/RequestWrapper.java
===================================================================
RCS file: src/org/eclipse/rwt/internal/service/wrapper/RequestWrapper.java
diff -N src/org/eclipse/rwt/internal/service/wrapper/RequestWrapper.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/rwt/internal/service/wrapper/RequestWrapper.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,261 @@
+/*******************************************************************************
+ * Copyright (c) 2009 EclipseSource and others. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution, 
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   EclipseSource - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.rwt.internal.service.wrapper;
+
+import java.io.*;
+import java.security.Principal;
+import java.util.*;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.*;
+
+
+public class RequestWrapper implements HttpServletRequest {
+
+  private HttpServletRequest m_request;
+
+  public RequestWrapper(HttpServletRequest request) {
+    m_request = request;
+  }
+  
+  public String getAuthType() {
+    return m_request.getAuthType();
+  }
+
+  public String getContextPath() {
+    return m_request.getContextPath();
+  }
+
+  public Cookie[] getCookies() {
+    return m_request.getCookies();
+  }
+
+  public long getDateHeader( String arg0 ) {
+    return m_request.getDateHeader( arg0 );
+  }
+
+  public String getHeader( String arg0 ) {
+    return m_request.getHeader( arg0 );
+  }
+
+  public Enumeration getHeaderNames() {
+    return m_request.getHeaderNames();
+  }
+
+  public Enumeration getHeaders( String arg0 ) {
+    return m_request.getHeaders( arg0 );
+  }
+
+  public int getIntHeader( String arg0 ) {
+    return m_request.getIntHeader( arg0 );
+  }
+
+  public String getMethod() {
+    return m_request.getMethod();
+  }
+
+  public String getPathInfo() {
+    return m_request.getPathInfo();
+  }
+
+  public String getPathTranslated() {
+    return m_request.getPathTranslated();
+  }
+
+  public String getQueryString() {
+    return m_request.getQueryString();
+  }
+
+  public String getRemoteUser() {
+    return m_request.getRemoteUser();
+  }
+
+  public String getRequestURI() {
+    return m_request.getRequestURI();
+  }
+
+  public StringBuffer getRequestURL() {
+    return m_request.getRequestURL();
+  }
+
+  public String getRequestedSessionId() {
+    // TODO: It is possible that this method should be reimplemented
+    return m_request.getRequestedSessionId();
+  }
+
+  public String getServletPath() {
+    return m_request.getServletPath();
+  }
+
+  public HttpSession getSession() {
+    return SessionManager.getInstance().getSession( m_request );
+  }
+
+  public HttpSession getSession( boolean arg0 ) {
+    if( arg0 ) {
+      HttpSession httpSession = m_request.getSession( true );
+      SessionWrapper sessionWrapper = SessionManager.getInstance().getSession( m_request );
+      if( sessionWrapper == null ) {
+        sessionWrapper = new SessionWrapper(
+          httpSession,
+          m_request.getParameter( SessionManager.PARAMETER_INSTANCE_ID ) );
+        SessionManager.getInstance().boundSession( sessionWrapper );
+      }
+      return sessionWrapper;
+    }
+    return getSession();
+  }
+
+  public Principal getUserPrincipal() {
+    return m_request.getUserPrincipal();
+  }
+
+  public boolean isRequestedSessionIdFromCookie() {
+    return m_request.isRequestedSessionIdFromCookie();
+  }
+
+  public boolean isRequestedSessionIdFromURL() {
+    return m_request.isRequestedSessionIdFromURL();
+  }
+
+  public boolean isRequestedSessionIdFromUrl() {
+    return m_request.isRequestedSessionIdFromUrl();
+  }
+
+  public boolean isRequestedSessionIdValid() {
+    return m_request.isRequestedSessionIdValid();
+  }
+
+  public boolean isUserInRole( String arg0 ) {
+    return m_request.isUserInRole( arg0 );
+  }
+
+  public Object getAttribute( String arg0 ) {
+    return m_request.getAttribute( arg0 );
+  }
+
+  public Enumeration getAttributeNames() {
+    return m_request.getAttributeNames();
+  }
+
+  public String getCharacterEncoding() {
+    return m_request.getCharacterEncoding();
+  }
+
+  public int getContentLength() {
+    return m_request.getContentLength();
+  }
+
+  public String getContentType() {
+    return m_request.getContentType();
+  }
+
+  public ServletInputStream getInputStream() throws IOException {
+    return m_request.getInputStream();
+  }
+
+  public String getLocalAddr() {
+    return m_request.getLocalAddr();
+  }
+
+  public String getLocalName() {
+    return m_request.getLocalName();
+  }
+
+  public int getLocalPort() {
+    return m_request.getLocalPort();
+  }
+
+  public Locale getLocale() {
+    return m_request.getLocale();
+  }
+
+  public Enumeration getLocales() {
+    return m_request.getLocales();
+  }
+
+  public String getParameter( String arg0 ) {
+    String value = m_request.getParameter( arg0 );
+    if( value == null ) {
+      value = SessionManager.getParameterFromReferer( m_request, arg0 );
+    }
+    return value;
+  }
+
+  public Map getParameterMap() {
+    return m_request.getParameterMap();
+  }
+
+  public Enumeration getParameterNames() {
+    return m_request.getParameterNames();
+  }
+
+  public String[] getParameterValues( String arg0 ) {
+    return m_request.getParameterValues( arg0 );
+  }
+
+  public String getProtocol() {
+    return m_request.getProtocol();
+  }
+
+  public BufferedReader getReader() throws IOException {
+    return m_request.getReader();  }
+
+  public String getRealPath( String arg0 ) {
+    return m_request.getRealPath( arg0 );
+  }
+
+  public String getRemoteAddr() {
+    return m_request.getRemoteAddr();
+  }
+
+  public String getRemoteHost() {
+    return m_request.getRemoteHost();
+  }
+
+  public int getRemotePort() {
+    return m_request.getRemotePort();
+  }
+
+  public RequestDispatcher getRequestDispatcher( String arg0 ) {
+    return m_request.getRequestDispatcher( arg0 );
+  }
+
+  public String getScheme() {
+    return m_request.getScheme();
+  }
+
+  public String getServerName() {
+    return m_request.getServerName();
+  }
+
+  public int getServerPort() {
+    return m_request.getServerPort();
+  }
+
+  public boolean isSecure() {
+    return m_request.isSecure();
+  }
+
+  public void removeAttribute( String arg0 ) {
+    m_request.removeAttribute( arg0 );
+  }
+
+  public void setAttribute( String arg0, Object arg1 ) {
+    m_request.setAttribute( arg0, arg1 );
+  }
+
+  public void setCharacterEncoding( String arg0 )
+    throws UnsupportedEncodingException
+  {
+    m_request.setCharacterEncoding( arg0 );
+  }
+}
Index: src/org/eclipse/rwt/internal/service/wrapper/SessionWrapper.java
===================================================================
RCS file: src/org/eclipse/rwt/internal/service/wrapper/SessionWrapper.java
diff -N src/org/eclipse/rwt/internal/service/wrapper/SessionWrapper.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/rwt/internal/service/wrapper/SessionWrapper.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * Copyright (c) 2009 EclipseSource and others. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution, 
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   EclipseSource - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.rwt.internal.service.wrapper;
+
+import java.util.*;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionContext;
+
+public class SessionWrapper implements HttpSession {
+
+  private static final String INSTANCE_ID_ATTRIBUTE_NAME = "instanceId";
+  private static final String MESSAGE_ILLEGAL_STATE_EXCEPTION = "RAP code should not call this method";
+  private static final String SEPARATOR = "@";
+  
+  private HttpSession m_httpSession;
+  private String m_instanceId;
+  
+  private long m_lastAccessedTime;
+
+  public SessionWrapper( HttpSession session, String instanceId ) {
+    m_httpSession = session;
+    m_instanceId = instanceId;
+    touch();
+  }
+  
+  public void touch() {
+    m_lastAccessedTime = new Date().getTime();
+  }
+  
+  private String getEncodedName(String name) {
+    return m_instanceId + SEPARATOR + name;
+  }
+  
+  private String getDecodedName(String name) {
+    String prefix = m_instanceId + SEPARATOR;
+    if (name.startsWith(prefix)) {
+      name = name.substring( prefix.length() );
+    }
+    return name;
+  }
+
+  public Object getAttribute( String arg0 ) {
+    touch();
+    if (INSTANCE_ID_ATTRIBUTE_NAME.equals( arg0 )) {
+      return m_instanceId;
+    }
+    return m_httpSession.getAttribute( getEncodedName( arg0 ) );
+  }
+
+  public Enumeration getAttributeNames() {
+    touch();
+    List list = new LinkedList();
+    for(Enumeration enumeration = m_httpSession.getAttributeNames(); enumeration.hasMoreElements(); ) {
+      String name = ( String )enumeration.nextElement();
+      list.add( getDecodedName( name ) );
+    }
+
+    final Iterator iterator = list.iterator();
+    return new Enumeration() {
+      public boolean hasMoreElements() {
+        return iterator.hasNext();
+      }
+
+      public Object nextElement() {
+        return iterator.next();
+      }
+    };
+  }
+
+  public long getCreationTime() {
+    throw new IllegalStateException(MESSAGE_ILLEGAL_STATE_EXCEPTION);
+  }
+
+  public String getId() {
+    return m_instanceId + SEPARATOR + m_httpSession.getId();
+  }
+
+  public long getLastAccessedTime() {
+    return m_lastAccessedTime;
+  }
+
+  public int getMaxInactiveInterval() {
+    throw new IllegalStateException(MESSAGE_ILLEGAL_STATE_EXCEPTION);
+  }
+
+  public ServletContext getServletContext() {
+    // TODO: Provide some extra-logic
+    return m_httpSession.getServletContext();
+  }
+
+  public HttpSessionContext getSessionContext() {
+    throw new IllegalStateException(MESSAGE_ILLEGAL_STATE_EXCEPTION);
+  }
+
+  public Object getValue( String arg0 ) {
+    throw new IllegalStateException(MESSAGE_ILLEGAL_STATE_EXCEPTION);
+  }
+
+  public String[] getValueNames() {
+    throw new IllegalStateException(MESSAGE_ILLEGAL_STATE_EXCEPTION);
+  }
+
+  public void invalidate() {
+    /*
+     * TODO: This method is using in tests only. May be in the future this
+     * method should be implemented.
+     */
+    throw new IllegalStateException(MESSAGE_ILLEGAL_STATE_EXCEPTION);
+  }
+
+  public boolean isNew() {
+    touch();
+    return m_httpSession.isNew();
+  }
+
+  public void putValue( String arg0, Object arg1 ) {
+    throw new IllegalStateException(MESSAGE_ILLEGAL_STATE_EXCEPTION);
+  }
+
+  public void removeAttribute( String arg0 ) {
+    touch();
+    m_httpSession.removeAttribute( getEncodedName(arg0) );
+  }
+
+  public void removeValue( String arg0 ) {
+    touch();
+    throw new IllegalStateException(MESSAGE_ILLEGAL_STATE_EXCEPTION);
+  }
+
+  public void setAttribute( String arg0, Object arg1 ) {
+    touch();
+    m_httpSession.setAttribute( getEncodedName(arg0), arg1 );
+  }
+
+  public void setMaxInactiveInterval( int arg0 ) {
+    throw new IllegalStateException(MESSAGE_ILLEGAL_STATE_EXCEPTION);
+  }
+  
+  HttpSession getWrappedHttpSession() {
+    touch();
+    return m_httpSession;
+  }
+  
+  String getInstanceId() {
+    touch();
+    return m_instanceId;
+  }
+}
Index: src/org/eclipse/rwt/internal/service/wrapper/SessionManager.java
===================================================================
RCS file: src/org/eclipse/rwt/internal/service/wrapper/SessionManager.java
diff -N src/org/eclipse/rwt/internal/service/wrapper/SessionManager.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/rwt/internal/service/wrapper/SessionManager.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2009 EclipseSource and others. All rights reserved.
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution, 
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   EclipseSource - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.rwt.internal.service.wrapper;
+
+import java.util.*;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+public class SessionManager {
+
+  public static final String PARAMETER_INSTANCE_ID = "instanceId";
+  public static final String HEADER_REFERER = "referer";
+  public static final String URL_DELIMETER = "&";
+  
+  private static SessionManager s_instance = new SessionManager();
+  private Map m_sessionsMaps = new HashMap();
+  private List m_sessionsList = new LinkedList();
+
+  public static SessionManager getInstance() {
+    return s_instance;
+  }
+  
+  private SessionManager() {
+  }
+
+  public synchronized void boundSession( SessionWrapper session ) {
+    Map sessionsMap = ( Map )m_sessionsMaps.get( session.getWrappedHttpSession().getId() );
+    if( sessionsMap == null ) {
+      sessionsMap = new HashMap();
+      m_sessionsMaps.put( session.getWrappedHttpSession().getId(), sessionsMap );
+    }
+    sessionsMap.put( session.getInstanceId(), session );
+    m_sessionsList.add( session );
+  }
+
+  public synchronized SessionWrapper getSession(HttpSession httpSession, String instanceId ) {
+    Map sessionsMap = ( Map )m_sessionsMaps.get( httpSession.getId() );
+    if( sessionsMap == null ) {
+      return null;
+    }
+    return ( SessionWrapper )sessionsMap.get( instanceId );
+  }
+  
+  public synchronized SessionWrapper getSession(HttpSession httpSession, String instanceId, boolean create ) {
+    if (!create) {
+      return getSession( httpSession, instanceId );
+    }
+    SessionWrapper session = new SessionWrapper(httpSession, instanceId);
+    boundSession( session );
+    return session;
+  }
+  
+  public synchronized SessionWrapper getSession(HttpServletRequest request) {
+    String instanceId = request.getParameter( PARAMETER_INSTANCE_ID );
+    if( instanceId == null ) {
+      Object obj = request.getAttribute( PARAMETER_INSTANCE_ID );
+      if( obj != null ) {
+        instanceId = String.valueOf( obj );
+      }
+    }
+    if( instanceId == null ) {
+      instanceId = getParameterFromReferer(request, PARAMETER_INSTANCE_ID);
+    }
+    HttpSession httpSession = request.getSession();
+    return getSession( httpSession, instanceId );
+  }
+  
+  static String getParameterFromReferer( HttpServletRequest request, String name ) {
+    String value = null;
+    String str = null;
+    String referer = request.getHeader( HEADER_REFERER );
+    if( referer != null ) {
+      int start = referer.indexOf( name );
+      if( start > -1 ) {
+        str = referer.substring( start + name.length() + 1 );
+        int end = str.indexOf( URL_DELIMETER );
+        if( end > -1 ) {
+          str = str.substring( 0, end );
+        }
+        value = str;
+      }
+    }
+    return value;
+  }
+  
+  public synchronized void unboundSession( SessionWrapper session ) {
+    HttpSession httpSession = session.getWrappedHttpSession();
+    Map sessionsMap = ( Map )m_sessionsMaps.get( httpSession );
+    if( sessionsMap != null ) {
+      sessionsMap.remove( session.getInstanceId() );
+      if( sessionsMap.size() == 0 ) {
+        m_sessionsMaps.remove( httpSession );
+      }
+    }
+    m_sessionsList.remove( session );
+  }
+  
+  
+  synchronized List getSessions() {
+    List arrayList = new ArrayList(m_sessionsList);
+    return arrayList;
+  }
+}