Thursday, January 29, 2015

External log4j configuration in WebSphere AS7

TL;DR - Zip your log4j.xml into an archive, and assign it as a Shared Library reference in an isolated ClassLoader for the deployed ear file.

We had all our configurations stored somewhere in our single EAR file. Somebody didn't like it this way, and we had to externalise the logging properties.

Our interim solution of setting the -Dlog4j.configuration= into the JVM runtime parameters was not well received. My next idea was to make use of the WebSphere Variables, but this old article proved that it will be costly to implement the AdminOperations MBean. This did not help either.

My colleague had attempted to reference the log4j XML file directly in the Shared Libraries reference, but did not succeed in getting WebSphere to adopt the properties. I tried referencing ${LOG_DIR} within the XML file, to a JVM custom property linked to the WebSphere ${LOG_ROOT} also to no avail.

What finally did work, was this:
  1. I extracted the log4j.xml from our EAR,
  2. Zipped this lone XML into an archive, 
  3. Placed it on the server in a path accessible by WebSphere,
  4. Create a Shared Library (WebSphere > Environment > Shared Libraries) mapping the classpath to this archive,
  5. Make sure to select "Use an isolated class loader for this shared library" or you'd have to assign reference for each module of your application rather than just the parent EAR.
  6. Assigning the reference within our EAR (WebSphere > ...Applications... > References > Shared library references),
  7. Restart the server node
Checks via the Class loader viewer (WebSphere > Troubleshooting > Class loader viewer) proved that the lone XML in our newly added archive is loaded for our application, overriding what we'd placed within the EAR file.

An additional tip useful for deployment I found was this. Simply put, the deployment.xml for WebSphere I had created (probably deserves another post after this) just needed another entry added as a child node of the <classloader> module to our EAR file:
<libraries libraryName="Log4j_XML" sharedClassloader="true"/>

Adding this would relieve us of extra steps that can potentially be missed out during deployment, because the shared library would already be configured by default. Keep in mind though, that the libraryName has to be an exact match to what was configured in the WebSphere Shared Libraries.

Tuesday, January 20, 2015

Skipping the java:/comp/env/jdbc context in JBoss AS7

The project started off on JBoss but we're now in mid-swing towards WebSphere. The properties file for the JDBC configuration has the significant difference in the JNDI context to the databases.

The version for JBoss originally was
datasource.jndi=java:/comp/env/jdbc/ourDS

The new one in use for WebSphere will be
datasource.jndi=jdbc/ourDS

Jetty that ran on my development localhost machine, found both versions acceptable without complaining. Thus, I sought to level the playing field such that our project could comfortably support either Application Server without too big a variation.

A number of solutions I'd found catered to the older versions of JBoss 4/5. They are not applicable because of the major change of the configuration XML since AS7. This is how the datasource configuration looks like for AS7.

Initially, I found this, and then this. It led me to conclude that there is a "resourceRef" flag that I could use. I tested adding the <property name="resourceRef" value="true"/> into the XML to no avail. As I began learning more about this topic, I gathered from the hints that a reference anchor was needed. I just had to figure out the correct place to do this. Since our configuration for JBoss required a standalone.xml to be located on the application server, I started digging there. And from my recent experiences, this was the biggest game-changer since earlier versions of JBoss.

An official documentation I found pointed to usage of naming bindings. I tried locating the detailed configuration for this Naming subsystem. Taking a potshot, I added this line:

<subsystem xmlns="urn:jboss:domain:naming:1.1">
    <bindings>
        <lookup name="jdbc/ourDS" lookup="java:/comp/env/jdbc/ourDS"/>
    </bindings>
</subsystem>

And it worked!

Note that there was already a separate configuration of the datasources in the Datasources subsystem configuration within this XML file. And with this, the properties file for the JDBC setup will not require the additional java:/comp context that only JBoss is looking for. Have fun!

Monday, January 19, 2015

jQuery BlockUI toggle detection

I realised that our JavaScript function that's calling blockUI was triggering the overlay regardless if it was already activated. This caused the grayed out screen to flash to white and back to gray.

As suggested on http://stackoverflow.com/questions/7907013/jquery-blockui-tell-if-page-or-specific-element-is-blocked, I made a variation of the posted solution of
var data = $('#element').data();
//will return Object like: { blockUI.isBlocked=1, blockUI.onUnblock=null} 

if (data["blockUI.isBlocked"] == 1)
// is blocked
else
// is not blocked
The '#element' bit was not applicable to me as much than if I were to instead use
var data = $(window).data();
if (data["blockUI.isBlocked"] == 1) {
  // is blocked
}
 The overlay was on the entire window and thus would otherwise have returned an empty [Object object] on the document alone.

Thursday, January 15, 2015

Loading DLL in a web-based applet: Part 2

I'd embarked on this mission a couple of months ago to get an applet working.

The downside to that earlier solution, was the requirement to amend the java.policy file on the client machine.

TL;DR -
  1. Run while-loop in applet;
  2. Poll webpage for action flag;
  3. Map public methods to equivalent JavaScript object;
  4. Break JavaScript function into pre-phase and post-phase;
  5. Instruct applet to trigger post-phase upon completion.

The permission that had to be added could well have been AllPermission if I hadn't took the time to drill down into getting the more specific Permissions needed:

grant {
    permission java.util.PropertyPermission "mapAnyUriToUri", "read";
    permission java.lang.RuntimePermission "accessDeclaredMembers";
    permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
};

How I dug these gems out was by a somewhat tedious combination of
  1. Telling the Java console to load verbose (Java Control Panel > Advanced tab > Debugging > Enable tracing), where all the permissions being checked for are output into the console, and
  2. Generally setting the JRE to debug mode (-Djava.compiler=NONE -Xnoagent -Xdebug -Xrunjdwp:transport=dt_socket,address=2502,server=y,suspend=y) in conjunction with connecting via my Eclipse IDE with the advice from here.

That said, I eventually realised after testing, that the above triplet of Permissions was set for the sake of letting JavaScript call the applet without being thrown PrivilegeActionExceptions. My misconception was that the permissions were to allow my applet to call the DLL when in fact, it was already happily doing its job within the chunk of doPrivileged code I followed from here.

As it turned out, this method did not sit well with my peers, so I had to refactor it one way or another. I scoured the web for solutions and this is what I managed to gather from hints all over:
  • This tightened security only began around Java 7u51;
  • Signed applets can call unsecured JavaScript functions happily;
  • JavaScript functions are impossible to be secured/signed thus universally untrusted;
  • This means security limitations would disallow JavaScript to trigger applet methods directly (unless the Permissions above were granted globally);
  • A forum post hinted at running a while-loop thread in the applet that polls the webpage.
And here's the eventual change of plans I had in place:
  • Test starting a Thread according to this;
  • Realised the DLL would end up in a separate ClassLoader as a result;
  • Maintained the run() within the applet itself (aka without starting it in a separate Thread, and this is achievable only because the applet merely runs in the background and doesn't have any UI component);
  • This run() acts as a listener of sorts, polling the webpage for requests to perform tasks;
  • The direct references to the applet on the webpages were transitioned to map to a JavaScript object;
  • This object (contained in its own .js file) is basically a JavaScript variation of the method calls I'd implemented in the applet;
  • Each function of this object passes in a callback method that the applet will trigger upon completion of its part.
This is how the JavaScript object looks like roughly:
//thisApplet.js
var thisApplet = {
    //fields
    flag: "",
    args: [],
    callback: "",
    propertyA: "",
    propertyB: "",
  
    //applet handles
    getFlag: function() {
        return this.flag;
    },
  
    getArgs: function() {
        return this.args;
    },
  
    doneSignal: function(retVar, propertyA, propertyB) {
        this.flag = "";
        this.args = [];
        this.propertyA = propertyA;
        this.propertyB = propertyB;
        if(this.callback != "") {
            this.callback(retVar);
        }
    },
  
    //properties called via JavaScript
    getPropertyA: function() {
        return this.propertyA;
    },
  
    getPropertyB: function() {
        return this.propertyB;
    },
  
    //applet-equivalent functions called via JavaScript
    login: function(userId) {
        this.flag = "login";
        this.args = [userId];
        this.callback = function(retVar) {
            loginCallback(retVar);
        };
    },
  
    logout: function() {
        this.flag = "logout";
        this.args = [];
        this.callback = "";
    }
  
};
 The JS source has been sanitised, naturally.

Within the applet, I have a method for parsing the args array properly:
private String[] parseJSObjectArray(JSObject jso) {
    String[] args = null;
    Object len = jso.getMember("length");
    if(len instanceof Number) {
        int n = ((Number)len).intValue();
        args = new String[n];
        for(int i=0;i<args.length;i++) {
            args[i] = jso.getSlot(i).toString();
        }
       
    }
   
    return args;
}

Doing so allows me to avoid changing the rest of the JavaScript calls that I had originally referencing to the applet as much. And now with the added benefit of not requiring to amend the java.policy file, too.

Thursday, January 1, 2015

Photoshop > Import Video to Layers on Windows 8.1

TL;DR - Install QuickTime 7.1.6, then update to 7.7.6

Having just migrated my Adobe Creative Suite 3 setup to a new laptop, I tried to extract frames from a video. At first it complained about "Could not complete the Video Frames to Layers command because the QuickTime version 7.1 or later is required." so I didn't think much of it.

And then I grabbed the latest installer off of https://www.apple.com/sg/quicktime/download/ easily enough. It's 2015 and any incompatibilities would have been resolved since Windows 8.1 came out... right? Wrong. Photoshop failed to detect my newly installed QuickTime 7.7.6. I tried "activating" it by launching the player manually, run with compatibility mode, reinstall with compatibility mode and even Run As Administrator.

Then I found this guy that had a similar situation. He mentioned that he stuck to QuickTime 7.1 and it works for him. I double-checked with my old laptop, but the version was 7.7.6 there. Regardless, I dug into the QuickTime download pages for older versions and found it. The installer worked and Photoshop was happy. But while it detected the older version, I wanted to match the version on my old laptop, so at the risk of breaking the setup, I updated QuickTime. It detected the new 7.7.6 and had me restart my machine.

I came back to Photoshop, lo and behold, it still worked. Now Photoshop can do what I want it to do, and I can rest easy knowing my version of QuickTime is up-to-date.

Happy New Year!