{"id":91,"date":"2007-11-01T13:56:25","date_gmt":"2007-11-01T18:56:25","guid":{"rendered":"http:\/\/www.dannorris.com\/2007\/11\/01\/using-mod_rewrite-to-rewrite-oc4j-served-urls-a-complete-review\/"},"modified":"2019-04-01T13:58:45","modified_gmt":"2019-04-01T13:58:45","slug":"using-mod_rewrite-to-rewrite-oc4j-served-urls-a-complete-review","status":"publish","type":"post","link":"https:\/\/www.dannorris.com\/blog\/2007\/11\/01\/using-mod_rewrite-to-rewrite-oc4j-served-urls-a-complete-review\/","title":{"rendered":"Using mod_rewrite to rewrite OC4J-served URLs &#8211; a complete review"},"content":{"rendered":"<p>\t\t\t\tWe recently ran into an issue in a customer configuration where rewriting URLs using pass-through didn\u2019t function as expected with OC4J-deployed applications. As it turned out, there\u2019s a bug in the OC4J container and a relatively easy workaround for some.<\/p>\n<p>The situation was this (names changed to protect the innocent):<\/p>\n<ul>\n<li>An existing Java application deployment existed using JRun on Solaris. In that deployment, an application called \u201cabc\u201d would be called as \u201chttp:\/\/abcapp.corp.com\/servlet\/login\u201d<\/li>\n<li>Applications were to be migrated to Oracle Application Server 10.1.3.1.0.<\/li>\n<li>Deployments on OAS were required to prefix the application with something and they used the application name. So, on the new site, the application would need to be called as \u201chttp:\/\/abcapp.corp.com\/abc\/servlet\/login\u201d. This was undesirable since bookmarks would have to be updated. While it could easily be handled with redirections, the desired behavior was to have all URLs match what they were on the old deployment.<\/li>\n<\/ul>\n<p>On the surface, this seems like a relatively simple problem to solve using a RewriteRule with the [PT] option and few RewriteConds in the Apache configuration. That is, until you find the bug in OC4J that makes it impossible. First, let\u2019s review the configuration parameters.<!--more--><\/p>\n<pre>NameVirtualHost *:4430\n&lt;VirtualHost *:4430&gt;\n  ServerName abcapp.corp.com\n  Port 443\n  SimulateHttps On\n  AddCertHeader HTTPS\n  CustomLog \"|c:\\oracle\\product\\10.1.3\\OracleAS_1\\bin\\rotatelogs logs\/abcapp-80-http-access.log 86400 -300\" common\n  ErrorLog \"|c:\\oracle\\product\\10.1.3\\OracleAS_1\\bin\\rotatelogs logs\/abcapp-80-http-error.log 86400 -300\"\n  Alias \/abc-images c:\\abcapp\\imagesRewriteEngine On\n  RewriteRule ^\/$ https:\/\/abcapp.corp.com\/servlet\/login [R,L]\n  RewriteCond %{REQUEST_URI} !^\/abc\/\n  RewriteCond %{REQUEST_URI} !^\/abc-images\/\n  RewriteRule ^\/(.*) \/abc\/$1 [PT]\n&lt;\/VirtualHost&gt;<\/pre>\n<p>So, let\u2019s go line by line. First, we\u2019re using name-based virtual hosting here (<strong>NameVirtualHost<\/strong>), so we tell Apache to evaluate all incoming requests received on port 4430 by attempting to match the <strong>ServerName <\/strong>from one of the VirtualHost sections (for port 4430) to the Host header in the HTTP request. If it finds a match, it serves the request from that VirtualHost. Note that it only matches the Host header which contains the hostname, but not a port number. So, you can\u2019t define the same name twice with two different port numbers unless they\u2019re using different VirualHost port numbers. That is, you can have the same name for two name-based VirtualHosts only if one of them is &lt;VirtualHost *:4430&gt; and the other is &lt;VirtualHost *:7777&gt;.<\/p>\n<p><strong>ServerName<\/strong> was explained above. <strong>Port <\/strong>is the port number that the end client (the browser) used when referencing this VirtualHost. It is the port used by the Apache server when it creates self-referential URLs and redirects (when only a URI is provided). No matter what port is specified in the VirtualHost and NameVirtualHost directive, the <strong>Port <\/strong>directive should always be the port used by the browser to access the site. That port may be on a webcache server, a load balancer, or a firewall.<\/p>\n<p><strong>SimulateHttps <\/strong>and <strong>AddCertHeader <\/strong>are directives used when your Apache server is not terminating the SSL connection. That is, a configuration like this: browser &gt; HTTPS &gt; load balancer\/webcache &gt; HTTP &gt; Apache HTTPD. In this case, SSL is \u201cstripped\u201d off by another device and only HTTP connections are handled by Apache HTTPD. So, you need to tell Apache that when it creates a self-referencing URL, it needs to use the https protocol instead of http in order for the client browser to connect properly. These parameters provide that directive. Note that in order to use the <strong>SimulateHttps <\/strong>directive, you\u2019ll first have to add the \u201cLoadModule certheaders_module libexec\/mod_certheaders.so\u201d (for UNIX) or \u201cLoadModule certheaders_module modules\/ApacheModuleCertHeaders.dll\u201d and \u201cAddModule mod_certheaders.c\u201d to your httpd.conf file first. See Metalink Note 378003.1 for more information.<\/p>\n<p><strong>CustomLog <\/strong>and <strong>ErrorLog <\/strong>are included inside the VirtualHost to get specific logging on the requests and errors encountered by this VirtualHost. These logs often help with debugging and are also sometimes used to track requests to this VirtualHost for reporting purposes as the application manager may want to know how many hits are coming in to their application.<\/p>\n<p>The <strong>Alias <\/strong>directive simply adds another virtual path to the virtual space Apache HTTPD will serve by mapping that virtual directory to a physical path on the local filesystem.<\/p>\n<p>Before we get in to the directives about URL rewriting, I\u2019d recommend reviewing the <a onclick=\"javascript:urchinTracker ('\/outbound\/article\/httpd.apache.org');\" href=\"http:\/\/httpd.apache.org\/docs\/1.3\/mod\/mod_rewrite.html\">reference documentation on mod_rewrite<\/a> and the <a onclick=\"javascript:urchinTracker ('\/outbound\/article\/httpd.apache.org');\" href=\"http:\/\/httpd.apache.org\/docs\/1.3\/misc\/rewriteguide.html\">URL Rewriting Guide<\/a> from the Apache 1.3 documentation. Oracle HTTP Server is based on Apache 1.3, so that\u2019s the relevant version for our work.<\/p>\n<p><strong>RewriteEngine <\/strong>enables the rewrite functionality. It is scoped within the VirutalHost, so even if it is defined elsewhere outside VirtualHosts, you\u2019ll need to include it in the VirtualHost.<\/p>\n<p>The first <strong>RewriteRule <\/strong>is a simple redirection to match the <a href=\"http:\/\/en.wikipedia.org\/wiki\/Regular_expressions\">regular expression<\/a> \u201c^\/$\u201d which literally means there is a slash (\/) at the beginning of the line (^) and with the end of the line ($) coming immediately after the slash. When that matches, we know that the request is for https:\/\/abcapp.corp.com\/ and nothing else. So, we need to redirect (that\u2019s the [R] at the end of the line) the browser to our application start page (https:\/\/abcapp.corp.com\/servlet\/login). The [R,L] are two separate arguments; the first argument (R) means that the response to the browser should be an \u201cexternal\u201d redirection and the browser will resubmit the reqeust to the target we send it, and the second argument (L) means that this is the last rule should be evaluated (if more rules are present). If this were the only rule here, we wouldn\u2019t need the L, but since more rules follow, this tells the rule engine to stop evaluating rules if this one matches (default behavior is to continue reading rules).<\/p>\n<p>The next few lines must be considered together. <strong>RewriteCond <\/strong>is a conditional statement that determines whether or not to evaluate the following <strong>RewriteRule<\/strong>. When more than one RewriteCond appears together, they are ANDed together by default. You can optionally OR them together\u2013check the docs for syntax. In our case, we want to prepend the application deployment prefix (\/abc) to all URLs that come through this VirtualHost unless they already have the prefix on the URL or if they\u2019re for some static, non-Java content that will be served by Apache directly (instead of being sent to OC4J for service). In our case, that means that our conditions are that the URL does *not* (exclamation point is the negation operator) start with \/abc\/ (the trailing slash is very important so we don\u2019t match all strings starting with \/abc) and also any URL that does *not* start with \/abc-images\/ (which is a static images directory that is Alias\u2019ed above). If the URL does *not* start with either one of those, then we\u2019ll go to the RewriteRule. The RewriteRule matches anything that starts with a slash (which is all requests) and the parens are a grouping operator that group the pattern \u201c.*\u201d. In regular expressions, the period (.) is a wildcard that matches any single character and the asterisk (*) is an operator that says match 0 or more of the previous character. So, this \u201c.*\u201d pattern says to match everything. In this case, the word \u201ccharacter\u201d means anything\u2013not just letters. When that pattern matches (and the parens will create a reference-able group for that pattern), the rule is instructing Apache to translate that to the string \/abc\/$1 which means to start the string with \/abc\/ and then put the stuff from the parens (earlier on this same line) on the end. Basically, prepend all requests with \/abc. For your reference, you can use multiple sets of parens on the same line and reference them (later on that same line) in order with $1, $2, etc. Finally, the [PT] at the end of this line tells Apache to apply this rule and then pass the resulting URL through to the rest of the Apache modules for handling. In our case, since \/abc is an application prefix, the mod_oc4j module will identify \/abc as an application and send the request to the proper OC4J for resolution.<\/p>\n<p>Here\u2019s the next problem, mod_oc4j does its job and sends the request to the proper OC4J for service. The OC4J, however, reviews the request by looking at the HTTP headers and sees that the client requested \/servlet\/login and since it doesn\u2019t have an application deployed at the prefix \/servlet, the OC4J container doesn\u2019t know how to handle this request, so it sends the request to the default application as defined in the default-web-site.xml file. This is a bug in OC4J and is filed as <a onclick=\"javascript:urchinTracker ('\/outbound\/article\/metalink.oracle.com');\" href=\"https:\/\/metalink.oracle.com\/metalink\/plsql\/f?p=130:15:862239773707049499::::p15_database_id,p15_docid,p15_show_header,p15_show_help,p15_black_frame,p15_font:BUG,5726819,1,1,1,helvetica\">Oracle bug 5726819<\/a>. So, the request makes it to the proper OC4J, so mod_oc4j did its job. However, the container satisfied the request from the default web application instead of the correct web application. In our case, we have only one application deployed to each container, so our workaround was to change the default web application to be our custom application (\/abc) instead of the application called \u201cdefault\u201d (which is automatically deployed in every OC4J container). That just involved changing these two lines in the %OH%\\j2ee\\OC4J_abc\\config\\default-web-site.xml file:<\/p>\n<pre>  &lt;default-web-app application=\u201ddefault\u201d name=\u201ddefaultWebApp\u201d root=\u201d\/j2ee\u201d \/&gt;\n  &lt;web-app application=\u201dabc\u201d name=\u201dabcapp\u201d load-on-startup=\u201dtrue\u201d root=\u201d\/abc\u201d \/&gt;<\/pre>\n<p>to these two lines instead (moving the \u201cdefault-\u201d):<\/p>\n<pre>  &lt;web-app application=\u201ddefault\u201d name=\u201ddefaultWebApp\u201d root=\u201d\/j2ee\u201d \/&gt;\n  &lt;default-web-app application=\u201dabc\u201d name=\u201dabcapp\u201d load-on-startup=\u201dtrue\u201d root=\u201d\/abc\u201d \/&gt;<\/pre>\n<p>In our case, on 10.1.3.1.0, that worked fine. We filed an SR anyway (we found this issue and resolution ourselves) to see if there was a patch or any unpublished workarounds that might be somehow better. What we learned was that there is another workaround that is available in 10.1.3.2.0 and 10.1.3.3.0 (not in our release of 10.1.3.1.0). That workaround is to put a new container environment variable in place on the start-parameters line in the opmn.xml. The variable is \u201c-Dajp.use.virgin.uri=false\u201d. If you attempt to put this in a 10.1.3.1.0 container, the container will fail to start, so it clearly was a customization added in 10.1.3.2.0 (apparently\u2013we didn\u2019t have luxury to test it on that version, but that\u2019s the guidance from our Oracle Support SR).<\/p>\n<p>Now, on to the next problem. Our application also used the Java session to store state information. That results in a session cookie (JSESSIONID) to be created and sent to the browser. All cookies contain a hostname and pathname that they should be used for. The client browser is required to only send cookies to the host and path that they have defined. That is, if the browser receives a cookie for abcapp.corp.com with path=\/abc, it is not allowed to send that cookie to any URLs unless they are on the site abcapp.abc.com and the URI path starts with \/abc. So, in our case, the OC4J (which creates the JSESSIONID cookie) thinks that the application path is \/abc, so it creates the cookie with path=\/abc. The browser receives it, stores it, but won\u2019t send it back because the browser is accessing a URI like \u201c\/servlet\/login\u201d or some other URI that doesn\u2019t start with \/abc. In order to tell OC4J to override the cookie path, you must use the <a onclick=\"javascript:urchinTracker ('\/outbound\/article\/download.oracle.com');\" href=\"http:\/\/download.oracle.com\/docs\/cd\/B25221_04\/web.1013\/b14426\/xmlfiles.htm#CJAGCICC\">&lt;session-tracking&gt; element<\/a>. This can be placed in the orion-web.xml file or the global-web-application.xml file, In our case, since we have just one application per OC4J, we defined it in the global-web-application.xml file. The &lt;session-tracker&gt; element must appear as a child of the &lt;orion-web-app&gt; element (which appears in both of the files I mentioned). In our global-web-application.xml file, we added this line just after the end of the opening &lt;orion-web-app&gt; tag (which is about 10 lines long) and before the &lt;mime-mappings&gt; tag:<\/p>\n<pre>  &lt;session-tracking cookie-path=\u201d\/\u201d&gt;<\/pre>\n<p>Additionally, now that all applications use the path of \u201c\/\u201d, if the same user wishes to access two different applications (which would be in two different OC4Js in our one-app-per-OC4J configuration), they will get a new JSESSIONID cookie that will collide with the previous one. If they use both applications simultaneously, their session information (and very likely their login session) on both applications will be affected. In order to have these applications share the JSESSIONID cookie, the OC4Js involved need to be instructed to look for a JSESSIONID cookie and use it for tracking the local session instead of creating its own JSESSIONID cookie. This is accomplished with another OC4J environment variable. By adding \u201c-Doracle.useSessionIDFromCookie=true\u201d to your start-options in the opmn.xml file for each OC4J, they will reuse the JSESSIONID cookie when it is found instead of creating a new one. See Metalink note 345167.1 for more on this configuration. Also note that while Metalink note 292972.1 has appropriate information describing the issue and a solution for the problem, that solution only applies to OAS versions 9.0.2 and 9.0.3 and *not* OAS 10g.<\/p>\n<p>That\u2019s it. Hopefully this will help someone resolve issues related to mod_oc4j, mod_rewrite, and java session management in such an environment.<\/p>\n<p>A few more notes to mention:<\/p>\n<ul>\n<li>This configuration was also load balanced with an F5 BIGIP and there are persistence settings needed on the BIGIP to ensure that the proper system is selected from the load balancer pools.<\/li>\n<li>We did not replicate session information or cluster the OC4Js in this environment. That\u2019s why it was critically important that the BIGIP persistence was working properly.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>We recently ran into an issue in a customer configuration where rewriting URLs using pass-through didn\u2019t function as expected with OC4J-deployed applications. As it turned out, there\u2019s a bug in the OC4J container and a relatively easy workaround for some. The situation was this (names changed to protect the innocent): An existing Java application deployment &hellip; <a href=\"https:\/\/www.dannorris.com\/blog\/2007\/11\/01\/using-mod_rewrite-to-rewrite-oc4j-served-urls-a-complete-review\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Using mod_rewrite to rewrite OC4J-served URLs &#8211; a complete review&#8221;<\/span><\/a><\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[29,15,22],"tags":[125,137,183,184,203],"class_list":["post-91","post","type-post","status-publish","format-standard","hentry","category-app-server","category-oracle","category-technical","tag-mod_rewrite","tag-oc4j","tag-rewritecond","tag-rewriterule","tag-simulatehttps"],"_links":{"self":[{"href":"https:\/\/www.dannorris.com\/blog\/wp-json\/wp\/v2\/posts\/91","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dannorris.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dannorris.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dannorris.com\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dannorris.com\/blog\/wp-json\/wp\/v2\/comments?post=91"}],"version-history":[{"count":1,"href":"https:\/\/www.dannorris.com\/blog\/wp-json\/wp\/v2\/posts\/91\/revisions"}],"predecessor-version":[{"id":574,"href":"https:\/\/www.dannorris.com\/blog\/wp-json\/wp\/v2\/posts\/91\/revisions\/574"}],"wp:attachment":[{"href":"https:\/\/www.dannorris.com\/blog\/wp-json\/wp\/v2\/media?parent=91"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dannorris.com\/blog\/wp-json\/wp\/v2\/categories?post=91"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dannorris.com\/blog\/wp-json\/wp\/v2\/tags?post=91"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}