by Mattias Lindskog - Java
The built in support for detecting proxies in Java is rather limited. In a recent project I ran into problems due to the lack of visible support for PAC (even though it is part of Java Web Start). In this post I outline how this can be solved by running JavaScript within a Java application and letting the JavaScript call methods defined in Java.
Before writing your own implementation it is worth noting that there are existing solutions available, e.g. Proxy Vole.
What is PAC?
Proxy auto-config was defined by Netscape for their Netscape Navigator 2.0 a long time ago (1996). It is a very dynamic method for configuring what proxy to use for accessing a specific URL. It works in the following way:
FindProxyForURL(url, host).FindProxyForURL(url, host) to get the proxy configuration to use. The configuration is in the form of a string with ; as separator, for example:|
1 |
PROXY proxy1.server.com:8080;DIRECT |
Using JavaScript from within Java to Evaluating PAC scripts
How can the FindProxyForURL(url,host) possibly find what URL to use? The answer is that it can use a number of pre-defined functions, see Navigator Proxy Auto-Config File Format. At first look the number of functions looks daunting but most of them can easily be implemented. We wanted to use Java as much as possible and therefore created a Java class holding methods with the same signatures as the JavaScript functions. It is called PACScriptMethods in the example code below which shows how the PAC script can be evaluated:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// First we get a JavaScript engine ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript"); // Create a Java binding to be used from the JavaScript execution engine.put("MyJavaPacImpl", new PACScriptMethods()); // Add the required JavaScript methods by bridging to the Java binding for(Method method : PACScriptMethods.class.getMethods()) { String bridgeFunctionDef = defineBridgeFunction( method.getName(), method.getParameterTypes().length); engine.eval(bridgeFunctionDef); } // The engine is now ready to be used to evaluate the PAC script // (passed in as a string) engine.eval(pacScript); // Now let's use the FindProxyForURL function to get the proxy // for the URL we want to access Invocable invocableEngine = (Invocable) engine; Object resultObj = invocableEngine.invokeFunction( "FindProxyForURL", url.toString(), url.getHost()); String proxyConfig = String.valueOf(resultObj); |
The careful reader may have spotted that I left out the defineBridgeFunction(...). It generates the strings defining JavaScript methods that simply call the method in our Java object. For example defineBridgeFunction("myFunction", 2) results in:
|
1 2 3 |
myFunction=function(arg0, arg1){ return MyJavaPacImpl.myFunction(arg0, arg1); } |
This uses the binding set on the JavaScript engine to call the corresponding Java method (same name and number of arguments).
The proxy configuration in proxyConfig can now easily be parsed (remember “;” as separator) to create a list of standard Java Proxy objects holding the proxy settings.