ICEfaces
  1. ICEfaces
  2. ICE-5871

Add a configuration parameter for turning on URL rewriting

    Details

    • Type: Improvement Improvement
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.8.2-EE-GA_P01, 1.8.2a
    • Fix Version/s: 1.8.3, 1.8.2-EE-GA_P02
    • Component/s: Bridge, Framework
    • Labels:
      None
    • Environment:
      Any
    • Affects:
      Compatibility/Configuration

      Description

      Currently, ICEfaces will detect that a client doesn't support cookies and will automatically switch to cookie-less mode where the session ID is transferred using request parameters rather than cookies. However, this currently only works when Ajax Push is not used as Ajax Push requires cookies.

      We have run into a situation where URL rewriting is required to support different sessions in multiple tabs/windows of the same browser instance but Ajax Push is also required. In this case, turning off cookies is not an options. Due to this requirement, we need a way to force ICEfaces into "cookieless mode" using a configuration parameter. This parameter will force ICEfaces to use URL rewriting instead of cookies for most requests but should also allow Ajax Push to function as cookies do not have to be turned off.

        Issue Links

          Activity

          Hide
          Deryk Sinotte added a comment -

          It turns out that simply adding a configuration parameter is not going to solve the problem. To properly do URL rewriting (encode the jsessionid parameter in the URL), you are supposed to use the HttpServletResponse.encodeURL method. This is exposed in the JSF via ExternalContext,encodeResourceURL. Each URL that needs to have the jsessionid written on to it should be run through this method. The JavaDoc for HttpServletResponse.encodeURL states that, to be robust, all URLs emitted by the servlet or framework should be run through this method. The container (e.g. Tomcat) is responsible for deciding whether or not to add the jsessionid. So the first step is to properly use the encodeResourceURL method for those URLs that the bridge uses to make various requests.

          Typically, the container detects whether or not to actually do the URL rewriting. The main scenario would be when the client has cookies disabled. However, it's possible that the container also has a switch for adjusting how the jsessionid is passed back and forth. For example, with Tomcat, you can specify as part of the Context configuration that cookies should not be used for jsessionid communication:

          <Context cookies="false">

          This allows you to leave cookies enabled in the client, but still use URL rewriting for passing the jsessionid.

          Show
          Deryk Sinotte added a comment - It turns out that simply adding a configuration parameter is not going to solve the problem. To properly do URL rewriting (encode the jsessionid parameter in the URL), you are supposed to use the HttpServletResponse.encodeURL method. This is exposed in the JSF via ExternalContext,encodeResourceURL. Each URL that needs to have the jsessionid written on to it should be run through this method. The JavaDoc for HttpServletResponse.encodeURL states that, to be robust, all URLs emitted by the servlet or framework should be run through this method. The container (e.g. Tomcat) is responsible for deciding whether or not to add the jsessionid. So the first step is to properly use the encodeResourceURL method for those URLs that the bridge uses to make various requests. Typically, the container detects whether or not to actually do the URL rewriting. The main scenario would be when the client has cookies disabled. However, it's possible that the container also has a switch for adjusting how the jsessionid is passed back and forth. For example, with Tomcat, you can specify as part of the Context configuration that cookies should not be used for jsessionid communication: <Context cookies="false"> This allows you to leave cookies enabled in the client, but still use URL rewriting for passing the jsessionid.
          Hide
          Deryk Sinotte added a comment -

          Attaching a patch that adjusts the DOMResponseWriter so that all the relevant URLs used by the bridge are run through the ExternalContext.getResourceURL method. This is done without using any configuration parameter because it will only be done when required - its irrelevant whether or not we are in synchronous mode. I have not applied this patch to the trunk yet because it doesn't quite work yet.

          After setting the Tomcat context to not use cookies for jsessionid and applying this patch, I ran Auction Monitor. The initial behaviour is that:

          1) The initial page loads and the page is built with the bridge configuration URI values having the jsessionid properly encoded.

          2) The first receive-updates request is made to the server and includes the jsessionid:

          http://localhost:8080/auctionMonitor/block/receive-updates;jsessionid=3D38C0FCF3D632CD190D70366E14FDBC

          3) The response from that request contains the new times as expected but also includes a new script update for the configuration values of the bridge. This includes new values for the URLs that don't included the encoded jsessionid:

          <update address="LsZblvYjadH6yftBebmHmg:1:configuration-script" tag="script"><attribute name="id"><Unable to render embedded object: File (1:configuration-script]]></attribute><attribute name="type"><) not found.[CDATA[text/javascript]]></attribute><content><![CDATA[window.disposeViewsURI = '/push-server/block/dispose-views';
          var container = 'LsZblvYjadH6yftBebmHmg:1:configuration-script'.asElement().parentNode;
          container.bridge = new Ice.Community.Application({optimizedJSListenerCleanup: false,session: 'LsZblvYjadH6yftBebmHmg',view: 1,synchronous: false,connectionLostRedirectURI: null,sessionExpiredRedirectURI: null,serverErrorRetryTimeouts: [1000,2000,4000], connection: {blockUI: false,context: '/auctionMonitor/', sendReceiveUpdatesURI: '/auctionMonitor/block/send-receive-updates',pingURI: '/auctionMonitor/block/ping',receiveUpdatesURI: '/auctionMonitor/block/receive-updates',receiveUpdatedViewsURI: '/push-server/block/receive-updated-views',heartbeat:

          {interval: 50000,timeout: 30000,retries: 3}

          ,timeout: 60000},messages: {sessionExpired: 'User Session Expired',connectionLost: 'Network Connection Interrupted',serverError: 'Server Internal Error',description: 'To reconnect click the Reload button on the browser or click the button below',buttonText: 'Reload'}}, container);]]></content></update>

          4) The next receive-updates request is then sent out without the jsessionid:

          http://localhost:8080/auctionMonitor/block/receive-updates

          5) This causes a <session-expired/> update and the Session Expired dialog is shown.

          So the current problem is that during some processing a diff is detected in the JavaScript configuration, likely because the jsessionid is not being encoded, and sending this update back. It's unclear why the jsessionid is not being encoded. It's likely somewhere in Tomcat's algorithm for how it determines why a URL gets a encoded.

          Show
          Deryk Sinotte added a comment - Attaching a patch that adjusts the DOMResponseWriter so that all the relevant URLs used by the bridge are run through the ExternalContext.getResourceURL method. This is done without using any configuration parameter because it will only be done when required - its irrelevant whether or not we are in synchronous mode. I have not applied this patch to the trunk yet because it doesn't quite work yet. After setting the Tomcat context to not use cookies for jsessionid and applying this patch, I ran Auction Monitor. The initial behaviour is that: 1) The initial page loads and the page is built with the bridge configuration URI values having the jsessionid properly encoded. 2) The first receive-updates request is made to the server and includes the jsessionid: http://localhost:8080/auctionMonitor/block/receive-updates;jsessionid=3D38C0FCF3D632CD190D70366E14FDBC 3) The response from that request contains the new times as expected but also includes a new script update for the configuration values of the bridge. This includes new values for the URLs that don't included the encoded jsessionid: <update address="LsZblvYjadH6yftBebmHmg:1:configuration-script" tag="script"><attribute name="id">< Unable to render embedded object: File (1:configuration-script]]></attribute><attribute name="type"><) not found. [CDATA [text/javascript] ]></attribute><content><![CDATA[window.disposeViewsURI = '/push-server/block/dispose-views'; var container = 'LsZblvYjadH6yftBebmHmg:1:configuration-script'.asElement().parentNode; container.bridge = new Ice.Community.Application({optimizedJSListenerCleanup: false,session: 'LsZblvYjadH6yftBebmHmg',view: 1,synchronous: false,connectionLostRedirectURI: null,sessionExpiredRedirectURI: null,serverErrorRetryTimeouts: [1000,2000,4000] , connection: {blockUI: false,context: '/auctionMonitor/', sendReceiveUpdatesURI: '/auctionMonitor/block/send-receive-updates',pingURI: '/auctionMonitor/block/ping',receiveUpdatesURI: '/auctionMonitor/block/receive-updates',receiveUpdatedViewsURI: '/push-server/block/receive-updated-views',heartbeat: {interval: 50000,timeout: 30000,retries: 3} ,timeout: 60000},messages: {sessionExpired: 'User Session Expired',connectionLost: 'Network Connection Interrupted',serverError: 'Server Internal Error',description: 'To reconnect click the Reload button on the browser or click the button below',buttonText: 'Reload'}}, container);]]></content></update> 4) The next receive-updates request is then sent out without the jsessionid: http://localhost:8080/auctionMonitor/block/receive-updates 5) This causes a <session-expired/> update and the Session Expired dialog is shown. So the current problem is that during some processing a diff is detected in the JavaScript configuration, likely because the jsessionid is not being encoded, and sending this update back. It's unclear why the jsessionid is not being encoded. It's likely somewhere in Tomcat's algorithm for how it determines why a URL gets a encoded.
          Hide
          Ted Goddard added a comment -

          Should we cache the initial state of the JavaScript config? What parameters do we currently allow being changed during runtime?

          Show
          Ted Goddard added a comment - Should we cache the initial state of the JavaScript config? What parameters do we currently allow being changed during runtime?
          Hide
          Deryk Sinotte added a comment -

          I thought about that as well. I'm not sure what we allow to be changed at runtime - if anything. There's actually a //todo comment in that part of the code that states basically the same thing:

          //todo: build startup script only once on aplication startup
          boolean synchronousMode = configuration.getAttributeAsBoolean("synchronousUpdate", false);
          String startupScript =
          "window.disposeViewsURI = '" + blockingRequestHandlerContext + "block/dispose-views';\n" +
          "var container = '" + configurationID + "'.asElement().parentNode;\n" +
          "container.bridge = new Ice.Community.Application({" +
          "optimizedJSListenerCleanup: " + configuration.getAttribute("optimizedJSListenerCleanup", "false") + "," +
          "session: '" + sessionIdentifier + "'," +
          "view: " + viewIdentifier + "," +
          "synchronous: " + synchronousMode + "," +
          "connectionLostRedirectURI: " + connectionLostRedirectURI + "," +
          "sessionExpiredRedirectURI: " + sessionExpiredRedirectURI + "," +
          "serverErrorRetryTimeouts: [" + configuration.getAttribute("serverErrorRetryTimeouts", "1000 2000 4000").trim().replaceAll("\\s+", ",") + "], " +
          "connection: {" +
          "blockUI: " + configuration.getAttribute("blockUIOnSubmit", "false") + "," +
          "context: '" + contextPath + "', " +
          (synchronousMode ?
          //encode path for URL rewrite session tracking mode
          ("sendReceiveUpdatesURI: '" + externalContext.encodeResourceURL(contextPath + "block/send-receive-updates") + "',") :
          ("sendReceiveUpdatesURI: '" + contextPath + "block/send-receive-updates" + "'," +
          "pingURI: '" + contextPath + "block/ping" + "'," +
          "receiveUpdatesURI: '" + contextPath + "block/receive-updates" + "'," +
          "receiveUpdatedViewsURI: '" + blockingRequestHandlerContext + "block/receive-updated-views" + "',") +
          "heartbeat:

          {" + "interval: " + configuration.getAttributeAsLong("heartbeatInterval", 50000) + "," + "timeout: " + configuration.getAttributeAsLong("heartbeatTimeout", 30000) + "," + "retries: " + configuration.getAttributeAsLong("heartbeatRetries", 3) + "}

          ,"
          ) +
          "timeout: " + configuration.getAttributeAsLong("connectionTimeout", 60000) +
          "}," +
          "messages:

          {" + "sessionExpired: '" + localizedBundle.getString("session-expired") + "'," + "connectionLost: '" + localizedBundle.getString("connection-lost") + "'," + "serverError: '" + localizedBundle.getString("server-error") + "'," + "description: '" + localizedBundle.getString("description") + "'," + "buttonText: '" + localizedBundle.getString("button-text") + "'" + "}

          " +
          "}, container);";

          Show
          Deryk Sinotte added a comment - I thought about that as well. I'm not sure what we allow to be changed at runtime - if anything. There's actually a //todo comment in that part of the code that states basically the same thing: //todo: build startup script only once on aplication startup boolean synchronousMode = configuration.getAttributeAsBoolean("synchronousUpdate", false); String startupScript = "window.disposeViewsURI = '" + blockingRequestHandlerContext + "block/dispose-views';\n" + "var container = '" + configurationID + "'.asElement().parentNode;\n" + "container.bridge = new Ice.Community.Application({" + "optimizedJSListenerCleanup: " + configuration.getAttribute("optimizedJSListenerCleanup", "false") + "," + "session: '" + sessionIdentifier + "'," + "view: " + viewIdentifier + "," + "synchronous: " + synchronousMode + "," + "connectionLostRedirectURI: " + connectionLostRedirectURI + "," + "sessionExpiredRedirectURI: " + sessionExpiredRedirectURI + "," + "serverErrorRetryTimeouts: [" + configuration.getAttribute("serverErrorRetryTimeouts", "1000 2000 4000").trim().replaceAll("\\s+", ",") + "] , " + "connection: {" + "blockUI: " + configuration.getAttribute("blockUIOnSubmit", "false") + "," + "context: '" + contextPath + "', " + (synchronousMode ? //encode path for URL rewrite session tracking mode ("sendReceiveUpdatesURI: '" + externalContext.encodeResourceURL(contextPath + "block/send-receive-updates") + "',") : ("sendReceiveUpdatesURI: '" + contextPath + "block/send-receive-updates" + "'," + "pingURI: '" + contextPath + "block/ping" + "'," + "receiveUpdatesURI: '" + contextPath + "block/receive-updates" + "'," + "receiveUpdatedViewsURI: '" + blockingRequestHandlerContext + "block/receive-updated-views" + "',") + "heartbeat: {" + "interval: " + configuration.getAttributeAsLong("heartbeatInterval", 50000) + "," + "timeout: " + configuration.getAttributeAsLong("heartbeatTimeout", 30000) + "," + "retries: " + configuration.getAttributeAsLong("heartbeatRetries", 3) + "} ," ) + "timeout: " + configuration.getAttributeAsLong("connectionTimeout", 60000) + "}," + "messages: {" + "sessionExpired: '" + localizedBundle.getString("session-expired") + "'," + "connectionLost: '" + localizedBundle.getString("connection-lost") + "'," + "serverError: '" + localizedBundle.getString("server-error") + "'," + "description: '" + localizedBundle.getString("description") + "'," + "buttonText: '" + localizedBundle.getString("button-text") + "'" + "} " + "}, container);";
          Hide
          Ted Goddard added a comment -

          Changes have been checked in to the ICEfaces trunk. auctionMonitor is verified working with multiple windows using a session per tab.

          To force tomcat to use URL rewriting rather than the JSESSIONID cookie add a META-INF/context.xml file to the .war containing:

          <?xml version="1.0" encoding="UTF-8"?>
          <Context cookies="false">
          </Context>

          It is necessary to use push-server.war or eps.war to obtain push functionality in a URL rewriting environment.

          Initial implementation with response.encodeURL() showed failures when encodeURL() was invoked on a response from a previous request. Caching the JavaScript configuration String from first use resolves this.

          Show
          Ted Goddard added a comment - Changes have been checked in to the ICEfaces trunk. auctionMonitor is verified working with multiple windows using a session per tab. To force tomcat to use URL rewriting rather than the JSESSIONID cookie add a META-INF/context.xml file to the .war containing: <?xml version="1.0" encoding="UTF-8"?> <Context cookies="false"> </Context> It is necessary to use push-server.war or eps.war to obtain push functionality in a URL rewriting environment. Initial implementation with response.encodeURL() showed failures when encodeURL() was invoked on a response from a previous request. Caching the JavaScript configuration String from first use resolves this.
          Hide
          Ken Fyten added a comment -

          QA testing of the Component Showcase using this new configuration shows numerous issues with specific components and functionality, likely related to URL mappings:

          • Cannot see charts
          • Download Resources does not work
          • File upload no button or link to choose file and no button to upload file
          • Google map link displays main page not gmaps
          • Richtext not displayed main page shown instead
          • Cannot change locale
          • Cannot change theme
          Show
          Ken Fyten added a comment - QA testing of the Component Showcase using this new configuration shows numerous issues with specific components and functionality, likely related to URL mappings: Cannot see charts Download Resources does not work File upload no button or link to choose file and no button to upload file Google map link displays main page not gmaps Richtext not displayed main page shown instead Cannot change locale Cannot change theme
          Hide
          Ted Goddard added a comment -

          These may be due to component bugs where the particular component does not expect ";jsessionid=..." in the URL, or where the component attempts to construct a URL from a base value. We may want to fix specific components on an as-needed basis.

          Show
          Ted Goddard added a comment - These may be due to component bugs where the particular component does not expect ";jsessionid=..." in the URL, or where the component attempts to construct a URL from a base value. We may want to fix specific components on an as-needed basis.
          Hide
          Ted Goddard added a comment -

          Since session-per-tab mode is only required in particular circumstances, not all components may be required in this mode. Individual JIRAs will be opened if any of the above components are found to be required in this mode.

          ICEfaces 2.0 should also be tested for session-per-tab use.

          Show
          Ted Goddard added a comment - Since session-per-tab mode is only required in particular circumstances, not all components may be required in this mode. Individual JIRAs will be opened if any of the above components are found to be required in this mode. ICEfaces 2.0 should also be tested for session-per-tab use.
          Hide
          Ted Goddard added a comment -

          Basic functionality is in place. If more advanced use is required that includes the known problematic components, individual JIRAs can be opened.

          Show
          Ted Goddard added a comment - Basic functionality is in place. If more advanced use is required that includes the known problematic components, individual JIRAs can be opened.

            People

            • Assignee:
              Ted Goddard
              Reporter:
              Deryk Sinotte
            • Votes:
              1 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: