Saturday, March 26, 2011

Running Apache as front-end to JBoss with virtual hosts

I run several websites on JBoss on my home computer and use Apache as a front-end to redirect requests to the correct sites. Basically if you want your site to be accessible from www.yourdomain.com as is without appending the web app context (e.g., www.yourdomain.com/yourapp) you need to do this. This is also more secure as you can bind JBoss to localhost only and hide the jmx-console.

First of all, you should have defined your web-apps' contexts in their WEB-INF/jboss-web.xml files:

<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>

  <context-root>/mywebapp</context-root>
</jboss-web>


Thats all what is needed from JBoss.

Next, you need to add the virtual host configs to Apache httpd.conf file. Example for a single website:

<VirtualHost *:80>
  ServerAdmin john@doe.com
  ServerName www.mysite.com
  ServerAlias mysite.com
  ProxyPass / http://localhost:8080/mywebapp/
  ProxyPassReverse / http://localhost:8080/mywebapp/
  ProxyPreserveHost On
  ProxyPassReverseCookiePath / /
  ErrorLog logs/mysite-error_log
  CustomLog logs/mysite-access_log common
</VirtualHost>

The thing with the proxypass and proxypassreverse directives is that it preserves the domain so you can handle cookies as is on JBoss side without any problems, and that sessions are tracked correctly.

JBoss and property files

I needed to use Java Properties in JBoss and found it somewhat difficult to find any decent documentation or examples how to do this, so here's how I got it working. It might not be the correct way, but it works.

Create your .properties file (key/value pairs) and place it in for example the WEB-INF dir. Load the props as follows:

Properties props = new Properties();
props.load(application.getResourceAsStream("/WEB-INF/mypropertiesfile.properties"));

Then load whatever you need.

Ratcliff/Obershelp pattern recognition in Java

I needed a way to compare the similarity of two strings and return value as a percentage in a project, so I implemented a Java version of the Ratcliff/Obershelp pattern regocnition algorithm and thought I'd share it here. The definition of the algorithm is as follows:

"Compute the similarity of two strings as the doubled number of matching characters divided by the total number of characters in the two strings. Matching characters are those in the longest common subsequence plus, recursively, matching characters in the unmatched region on either side of the longest common subsequence."

The returned value ranges from 0..1f. Below is the source, enjoy :)

public class Simil {

 public Simil() {};
 
 private static float tcnt;
 
 private void findSubstr(String s1, int s1len, String s2, int s2len, Struct ss) {
  int size = 1;
  
  ss.setO2(-1);
  
  for (int i = 0; i < (s1len - size); i++) {
   for (int j = 0; j < (s2len - size); j++) {
    int test_size = size;
    
    while (true) {
     if ((test_size <= (s1len - i)) && (test_size <= (s2len - j))) {
      if (s1.regionMatches(i, s2, j, test_size)) {
       if (test_size > size || ss.getO2() < 0) {
        ss.setO1(i);
        ss.setO2(j);
        size = test_size;
       }
       test_size++;
      } else {
       break;
      }
     } else {
      break;
     }
    }
   }
  }
  
  if (ss.getO2() < 0) {
   ss.setLen(0);   
  } else {
   ss.setLen(size);
  }
 }
 
 private void rsimil(String s1, int s1len, String s2, int s2len) {
  Struct ss = new Struct();
  
  if (s1len == 0 || s2len == 0) return;
  
  findSubstr(s1, s1len, s2, s2len, ss);
  
  if (ss.getLen() > 0) {
   int delta1, delta2;
   tcnt += ss.getLen() << 1;
   rsimil(s1, ss.getO1(), s2, ss.getO2());
   
   delta1 = ss.getO1() + ss.getLen();
   delta2 = ss.getO2() + ss.getLen();
   
   if (delta1 < s1len && delta2 < s2len) {
    rsimil(s1.substring(delta1, s1len), s1len - delta1, s2.substring(delta2, s2len), s2len - delta2);
   }
  }
  
 }
 
 public float ratcliff(String s1, String s2) {
  int s1len, s2len;
  float tlen;
  
  if (s1 == null || s2 == null) {
   return 0;
  } else if (s1.equals(s2)) {
   return 1;
  }
  
  s1 = s1.toLowerCase();
  s2 = s2.toLowerCase();
  
  s1len = s1.length();
  s2len = s2.length();
  
  tcnt = 0;
  tlen = s1len + s2len;
  
  rsimil(s1, s1len, s2, s2len);
  
  return tcnt / tlen;
 }
 
 class Struct {
  Struct() {};
  
  int o1, o2, len;

  public int getLen() {
   return len;
  }

  public void setLen(int len) {
   this.len = len;
  }

  public int getO1() {
   return o1;
  }

  public void setO1(int o1) {
   this.o1 = o1;
  }

  public int getO2() {
   return o2;
  }

  public void setO2(int o2) {
   this.o2 = o2;
  }  
 }
}

Enabling URL rewriting in JBoss

This isn't particularly difficult, but the information contained in JBoss docs is pretty vague. Basically to enable URL rewrites, you need to create a "context.xml" file and place it inside the WEB-INF -directory of your web app. In the context file, enable the rewrite valve as follows:

<context cookies="true" crosscontext="true">
    <valve classname="org.jboss.web.rewrite.RewriteValve">
</context>

Next, create a "rewrite.propreties" file and place it in the same directory. Add your rewrite rules there.
Nothing more to it really. The syntax is the same as in Apache's mod_rewrite, but JBoss doesn't support every feature there, at least I found that the RewriteLog directive is not supported.

Note that when you add or edit a rule, you need to restart your web app for it to take effect.

If you find that your rules are not working, its propably because your rule is wrong, not that the rewrites are not enabled. If you specify a rule with incorrect syntax you will get an exception in the JBoss console/server log when you (re)start your web app. Make very simple test rule and verify that it works, then start making it more complex and building up from that.