I had tried thinwire a little bit. Everything works fine and as expected, except multiple views of the same application.
IMHO it should be not a such big deal to extend thinwire in an appropriate way (SessionID + Parameter).
I think a user does not expect the current behavior and it is more natural to work with multiple views of the same application. To limit this to f.I. to a single login should be part of the application.
Hope this is not too offensive.
Logged In: YES
user_id=1719939
Originator: NO
For a possible (far from beeing perfect) workaround have a look at <http://sourceforge.net/forum/forum.php?thread_id=1858346&forum_id=584240>
Logged In: YES
user_id=1544509
Originator: NO
See discussion here: http://sourceforge.net/forum/forum.php?thread_id=1877571&forum_id=584240
Index: F:/devel/thinwire-1.2_SVN/src/thinwire/render/web/WebServlet.java
--- F:/devel/thinwire-1.2_SVN/src/thinwire/render/web/WebServlet.java (revision 588)
+++ F:/devel/thinwire-1.2_SVN/src/thinwire/render/web/WebServlet.java (working copy)
@@ -26,25 +26,24 @@
*/
package thinwire.render.web;
-import java.io.*;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.*;
+
import org.apache.commons.fileupload.DiskFileUpload;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import thinwire.ui.FileChooser;
-import javax.servlet.*;
-import javax.servlet.http.*;
-
/**
* @author Joshua J. Gertzen
*/
@@ -102,31 +101,55 @@
}
}
- public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
+ public void service(HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException
+ {
String method = request.getMethod();
-
+ String instanceId = null;
+ if ((request.getPathInfo() != null) && (request.getPathInfo().length() > 0))
+ instanceId = request.getPathInfo().substring(1);
+
if (method.equals("GET")) {
+ //Check instanceId
+ if ((instanceId == null) || (instanceId.length() == 0)) {
+ StringBuilder sbHash =
+ new StringBuilder(request.getRequestURI())
+ .append(request.getRemoteAddr())
+ .append(System.identityHashCode(request))
+ .append(System.currentTimeMillis())
+ .append(System.nanoTime());
+ instanceId = UUID
+ .nameUUIDFromBytes(sbHash.toString().getBytes()).toString();
+ StringBuilder sbRedirect = new StringBuilder(instanceId);
+ if (request.getQueryString() != null)
+ sbRedirect
+ .append("?")
+ .append(request.getQueryString());
+ response.sendRedirect(sbRedirect.toString());
+ return;
+ }
+
String resource = request.getParameter("_twr_");
if (resource == null) {
String path = request.getServletPath();
if (path == null || path.equals("") || path.equals("/")) {
- handleStart(request, response);
+ handleStart(request, response, instanceId);
} else {
if (log.isLoggable(Level.WARNING)) log.log(Level.WARNING, "invalid GET request with servletPath of '" + path + "', possible cause is an invalid resource reference in your application");
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
} else {
- handleResource(request, response, resource);
+ handleResource(request, response, resource, instanceId);
}
} else if (method.equals("POST")) {
String action = request.getParameter("_twa_");
if (action == null) {
- handlePostEvent(request, response);
+ handlePostEvent(request, response, instanceId);
} else if (action.equals("upload")) {
- handleUserUpload(request, response);
+ handleUserUpload(request, response, instanceId);
}
} else {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
@@ -209,11 +232,11 @@
return args;
}
- private void handleStart(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
+ private void handleStart(HttpServletRequest request, HttpServletResponse response, String instanceId) throws IOException, ServletException {
HttpSession httpSession = request.getSession();
String id = httpSession.getId();
- ApplicationHolder holder = (ApplicationHolder)httpSession.getAttribute("instance");
+ ApplicationHolder holder = (ApplicationHolder)httpSession.getAttribute("instance" + instanceId);
response.setContentType("text/html");
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache, no-store");
@@ -231,7 +254,7 @@
if (holder != null) {
if (log.isLoggable(LEVEL)) log.log(LEVEL, "removing existing application instance with id=" + id);
- httpSession.removeAttribute("instance");
+ httpSession.removeAttribute("instance" + instanceId);
}
}
@@ -255,12 +278,11 @@
}
holder.app = new WebApplication(this.getServletContext().getRealPath(""), mainClass, getInitParameter(InitParam.STYLE_SHEET.mixedCaseName()), args.toArray(new String[args.size()]));
- httpSession.setAttribute("instance", holder);
+ httpSession.setAttribute("instance" + instanceId, holder);
}
- private void handleResource(HttpServletRequest request, HttpServletResponse response, String resourceName) throws IOException, ServletException {
- ApplicationHolder holder = (ApplicationHolder)request.getSession().getAttribute("instance");
-
+ private void handleResource(HttpServletRequest request, HttpServletResponse response, String resourceName, String instanceId) throws IOException, ServletException {
+ ApplicationHolder holder = (ApplicationHolder)request.getSession().getAttribute("instance" + instanceId);
if (holder == null || holder.app == null) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "no application instance exists from which to retreive resources");
return;
@@ -306,9 +328,9 @@
}
}
- private void handlePostEvent(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
+ private void handlePostEvent(HttpServletRequest request, HttpServletResponse response, String instanceId) throws IOException, ServletException {
HttpSession httpSession = request.getSession();
- ApplicationHolder holder = (ApplicationHolder)httpSession.getAttribute("instance");
+ ApplicationHolder holder = (ApplicationHolder)httpSession.getAttribute("instance" + instanceId);
response.setContentType("text/plain; charset=utf-8");
response.setHeader("Cache-Control", "no-store");
if (holder == null || holder.app == null) return;
@@ -316,13 +338,21 @@
if (holder.app.state == WebApplication.State.TERMINATED) {
holder.app = null;
- httpSession.invalidate();
+ synchronized(httpSession) {
+ boolean otherInstances = false;
+ httpSession.removeAttribute("instance" + instanceId);
+ @SuppressWarnings("unchecked")
+ Enumeration<String> names = httpSession.getAttributeNames();
+ while (!otherInstances && names.hasMoreElements())
+ otherInstances = names.nextElement().startsWith("instance");
+ if (!otherInstances) httpSession.invalidate();
+ }
}
}
- private void handleUserUpload(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
+ private void handleUserUpload(HttpServletRequest request, HttpServletResponse response, String instanceId) throws IOException, ServletException {
HttpSession httpSession = request.getSession();
- ApplicationHolder holder = (ApplicationHolder)httpSession.getAttribute("instance");
+ ApplicationHolder holder = (ApplicationHolder)httpSession.getAttribute("instance" + instanceId);
if (holder.app != null) {
try {
@@ -351,6 +381,6 @@
}
}
- response.sendRedirect("?_twr_=FileUploadPage.html");
+ response.sendRedirect(instanceId + "?_twr_=FileUploadPage.html");
}
}
[Sorry, this will explain the previous post... I copied the wrong thing to the comments field]
I have a (kind-of) solution. Which involves still using the same session, but everytime someone logs into the main thinwire url, it creates an 'instance number' and redirects the url to that. EG: http://localhost/thinwire/ -> http://localhost/thinwire/dead-beef-cafe-babe .
This way there are multiple 'sessions' or (to be clearer) 'instances' within the session. All that needs to change is the webservlet which takes the remaining portion of the url (the instance id) and then looks up the application instance in the session based on that. If there is none, it can create one.
This works well with Refresh (F5), since it will have the url already set with the instance id and will work as per normal. But at the same time opening a new tab and going to http://localhost/thinwire/ will generate a new instance id since there is none attached to the url. The javascript XMLHttpRequest calls will still work because they call the originating url, so the instance id can also be derived from this.
We have been trying this out in a development, then testing environment, and we are happy with it and about to deploy it to a production environment.
Below is the patched code of what I did: