Monday, October 5, 2015

Using CascadeType with Hibernate Reverse Engineering Tool

As of Hibernate (version 4.3.11-Final) it seems, silly little me was trying to figure out how to annotate the entity beans with the cascade instruction. I didn't think it made sense to edit the PojoPropertyAccessor.ftl and the likes of those templates for this, much less manually edit the sources after they are generated. It was actually simpler than that, but maybe so straightforward nobody in recent years (up to 2013) bothered to describe how this works.

        <foreign-key constraint-name="fk_1_2" foreign-table="tbl_2">
            <column-ref local-column="id" foreign-column="id"/>
            <one-to-one property="a" cascade="merge" fetch="select"/>
        </foreign-key>

The Cascade will be annotated accordingly with your (all-lowercase) selection from either org.hibernate.annotations.CascadeType or javax.persistence.CascadeType; same difference.

Tuesday, September 29, 2015

Spring + Hibernate + Wildfly: Finding the right match

While setting up a new project, I had to do a bunch of trial-and-error, in order to get the WAR package deployable on the application server. These are my findings thus far:
  • Application Server: Wildfly 9.0.1.Final (formerly known as JBoss AS7)
  • Application Framework: Spring 4.2.1.RELEASE
    • spring-context
    • spring-webmvc
    • spring-orm
  • ORM Framework: Hibernate 4.3.11.Final
    • hibernate-core
    • hibernate-c3p0
    • hibernate-ehcache
Here are some of the problems I'd encountered, that I had to tackle as they appeared:
  1.  java.lang.IllegalStateException: Could not locate SessionFactory in JNDI
    • sessionFactoryName
      • Ensure your hibernate.cfg.xml has these properties
        • <session-factory name="mySessionFactory">
        • hibernate.jndi.url
        • hibernate.jndi.sessionFactoryName
  2. org.dom4j.DocumentFactory cannot be cast to org.dom4j.DocumentFactory
    • Wildfly Administration Console > Configuration
    • Subsystems > EE > View
    • EE Subsystem > Global Modules
    • Add
      • Name: org.dom4j
      • Slot: main (prepopulated by default)
    • Save
    • Restart Wildfly
    • What is not needed to be done:
      • Adding of "Dependencies: org.dom4j export" into MANIFEST
      • Manual removal/auto exclusion of dom4j JAR during Maven build
      • Adding META-INF/jboss-deployment-structure.xml
  3. JNDI/JDBC configuration
    • The naming conventions can get rather confusing at times, but here's what worked in Wildfly:
      • JndiObjectFactoryBean > jndiName = java:/jdbc/myDataSource
      • Wildfly Admin Console > Configuration
        • Subsystems > Datasources > View
        • Configure your datasource 
        • Ensure the JNDI name matches "java:/jdbc/myDataSource"
        • Note that Wildfly requires the "java:/" prefix
      • Edit: I have concluded that this is in fact no required as long the datasource name matches across your servlet-context.xml and the server configuration. Include this snippet in your web.xml
        • <resource-ref>
              <res-ref-name>jdbc/msbDataSource</res-ref-name>
              <res-type>javax.sql.DataSource</res-type>
              <res-auth>Container</res-auth>
          </resource-ref>
  4. org.hibernate.service.UnknownUnwrapTypeException: Cannot unwrap to requested type [javax.sql.DataSource]
    • This led me to this
    • In short, I had to configure the hibernate.cfg.xml to factor in settings for c3p0 as indicated
    • Meanwhile, I also noticed that my Hibernate versions were all over the place;
    • Adventurously, I experimented with Hibernate 5, or more specifically org.springframework.orm.hibernate5.HibernateTransactionManager to which came the complain from Wildfly: NoSuchMethodError ConnectionProviderInitiator.extractIsolation
    • This was when I decided to take a step back and 
      • reverted from Hibernate 5 to Hibernate 4;
      • and added dependency and configuration for c3p0;
    • Edit: I have isolated the cause of this occurrence during the Eclipsed Embedded Jetty deployment to be due to the presence of the "hibernate.connection.datasource" property in my hibernate.cfg.xml that seemingly requires the c3p0 set of properties. In short, don't bother including hibernate.connection.datasource since you will either already have it configured in the application server (e.g. Wildfly) or the jetty-env.xml file.
  5. Configuration for application context path 
    • This one was fairly straightforward:
    •  Create WEB-INF/jboss-web.xml with the following:
      <?xml version="1.0" encoding="UTF-8"?>
      <jboss-web>
         <context-root>/myapp</context-root>
      </jboss-web>
My exploration continues...

Tuesday, August 25, 2015

24 Tips for playing Fallout Shelter

All work and no play makes Jack a dull boy, so I need the occasional fun and games when I'm not working. Since they've just recently released the Android version of Fallout Shelter, I've taken to giving it a go.

As a typical gamer, I'd venture into the web to find strategies, tips and tricks for getting the most out of my gaming experience. I did come across several notable ones that provided a handful of good suggestions. But I realised that I can just as well document my findings regardless.

This is my list of observations:
Layout
  1. Assign all weapons to dwellers, keeping one powerful rifle in storage when possible; in case of random infestations in rooms with unarmed dwellers.
  2. Training rooms are sufficient in singles; it is possible thus to fit them in pairs on the right side of the map.
  3. Med Bays and Science Labs correlates to storage of Stimpaks and Radaways respectively; if you find yourself running out of space for storing these resources, build extra rooms for them without assigning dwellers to the rooms. These can be stacked with the training rooms.
  4. Rooms furthest away from Power Stations lose electricity first; build with the intention of sandwiching Power Stations between other resource rooms.
  5. Rooms touching dirt are where Mole rats strike first, according to this and also a tip from the loading screen; to prevent this, it is possible to build a second column of elevators since there isn't much space for anything else. Dwellers will use whichever elevator that is nearest.
Training
  1. Zoom in on a training room to see the countdown timer for dwellers to get an increment on the attribute they are training for.
  2. Training time is exponential across stat increments; unless you are prepared to wait extended hours to max the stat, it is ineffective to train a low-stat dweller.
  3. If training is interrupted, the timer resumes from where it left off instead of restarting.
Rushing
  1. A general guide for "safe" rushing would be for the Incident risk to be below 50%.
  2. Even if you've maxed out the storage for your resources, it is still possible to rush rooms in order to generate more caps; do this especially when the Incident risk is in the 20-30% range.
  3. Just before rushing a room, make sure the dwellers are sufficiently weaponised to prepare for infestations.
  4. If you notice the resource countdown timer reaching soon, wait for it to restart before activating a rush.
Reinforcements
  1. Assign more dwellers to help with larger fires to reduce the time taken in extinguishing the flames.
  2. Raiders attack starting from the vault door before proceeding downwards in a zigzag pattern. Assign dwellers to "receive" these unwelcome guests, starting from the first living quarters to preempt their next move.
  3. Support comes faster from dwellers working in adjacent rooms on the same floor.
  4. Even faster still, would be to reassign weapons from dwellers in other rooms to those that is encountering an incident.
Wasteland
  1. Send dwellers with high Endurance out into the wasteland; pack them off with 10 or more Stimpacks and Radaways and they will run the night while you sleep.
  2. It is possible to send a dweller who is totally unequipped into the wasteland. If they survive long enough, they will randomly encounter clothes and weapons along the way. If they are wearing the Vault Suit when they pick up their first piece of clothing, they will automatically wear the garment. If they leave the vault unarmed, they will likewise equip themselves with the first weapon they chance upon.
  3. The maximum Stimpacks and Radaways you can equip any one dweller is 25 of each. Do so only if you intend to let the dweller roam for extended hours. 
  4. They will automatically equip themselves with gear that they come across, which is better than what you'd sent them out with.
Babies
  1. Babies have their last names randomly picked from either parent; a convenient way of not pairing up families in future would be to name the boys after their mothers, and girls after their fathers.
  2. Throttle the baby-making to avoid a baby-boom; start with 2 to 3 pregnancies at any one time.
  3. Don't focus on reproducing couples with high Charisma; if you have a Nightwear, put it on a dweller working in the Radio Studio, and have every male dweller borrow it when needed. 
  4.  If you have training rooms already, it isn't as big a deal to pair couples with high attributes. All it takes is another few more hours/days of training to get the new kid up to speed with above-average stats.
Let's see if I can discover more anecdotes that I can add to the list!

Edit: I found this which led me to this amazing spreadsheet!

Sunday, June 14, 2015

HSM and the fellowship of cryptology

As luck would have it, I've been recently tasked to find a substitute for the Hardware Security Module that, for a handful of reasons, would no longer be able to serve us.

What is a HSM?
HSMs are server appliances, built on top of PKCS #11 in order to take advantage of the cryptographic functions, while maintaining a high level of security for the keys that it holds. The authentication, encryption, decryption, signing and all manner of connections to it are logged. They usually come with cryptographic security tokens that make sure you need to be physically present, to even perform any sensitive operation like reboots.

Sitting on the application server is an agent that would already have had a certificate exchange with the HSM appliance. The application need only to liaise with this agent, that will handle the communication with the HSM server.

The hunt
It would have been an easy job, had we only require asymmetric keys. Any old JKS, or PKCS #12 archive (for better compatibility outside of Java) stored within the application server would work. Except that our application require use of symmetric keys. The problem is, most keystores (a concept mostly within Java) only support private key and certificate pairs. They cannot handle storing secret keys.

Enter JCEKS. The Java Cryptographic Extension Key Store can carry private and secret keys alike. The next closest thing is from Bouncy Castle, with their BKS/UBER. A quick browse-through using KeyStore Explorer should suffice to show you their differences from JKS and PKCS #12. But I did take a little bit further to confirm that both Oracle and IBM variety of the JRE does indeed not support generating secret keys into PKCS #12 keystores, just because this claimed it possible. The runtime throws an exception because you'd have to pass in a null for the certificate, when PKCS #12 expects a certificate.

Any answers?
The valiant effort of OpenDNSSEC with providing a SoftHSM, proved too complicated beyond my comprehension. Whereas an Italian job - j4sign - requires an actual smart card as part of their recipe.

Somebody did suggest to encrypt the secret key with an asymmetric key kept in a keystore, and store it alongside in the filesystem. I see where he's coming from, but I'd feel naked and exposed not containing the key within the archive. It's plausible; just not ideal.

So what do?

I'm sticking with JCEKS for the time being. My counterparts working on C++ may need to put in some effort, while we sort this out. But to the best of my abilities, I've tried providing a wrapper JAR (that basically behaves like the HSM agent) for them to take advantage of the JCE keystore. It's still a work in progress, so if I can't convince them to make it work, we'll need to come up with an alternative, and fast.

The journey continues!

If you have any inkling of all these shenanigans, hit me up and let me know your thoughts.

Friday, April 10, 2015

jaxb.properties

A client was looking through the JRE console output with trace enabled. He saw the network communication going on while JAXB was trying to locate an implementation for the SAXParserFactory that I have included in my applet. It looked a bit like this:

network: Connecting https://my.url.com/my-project/com/project/result/jaxb.properties with proxy=DIRECT

In essence, the JRE tries searching for the file in-depth from the server repeatedly, causing what he amounted to increased latency across the board when accessing our application. I looked up the javadoc for SAXParserFactory on the sequence of events. This place spelled out the default implementation that's internal the Java. I simply placed into the annoying jaxb.properties file the following line:

javax.xml.bind.context.factory=com.sun.xml.internal.bind.v2.ContextFactory 

This jaxb.properties file was added into the several locations that I saw popping up in my Java console that has a relative path from my applet project. The runtime found what it's looking for, and no longer searches for the same file in multiple other locations. 

I'm aware that this file is leftover from the earlier versions of JAXB, but if Java is still churning out extra traffic while trying to hunt down a non-existent file, there will bound to be people that would much rather not have this extra step involved.

Edit: Apparently there's an even better way! This solution cuts through all the deprecated nonsense, by basically telling the applet to not do any lookups at all! 

The Java console log output for JRE 7 (not visible in JRE 8) indicated the search was still on, but for META-INF/services/javax.xml.parsers.SAXParserFactory this time. In order to avoid adding extraneous stuff entirely, just add this param into your HTML:
<applet>
  <param name="codebase_lookup" value="false">
</applet>

And it's even more convenient this way!

Thursday, March 26, 2015

Changes to applet JAR manifests for Java 7 onwards

Reading through this official page made me realise that it was not being specific about what needs to be done. The changes are being categorised into each Update without summarising what is the latest set of TODOs in order for your applet to be able to run properly. Here's my TL;DR of this page:
  • MANIFEST.MF (required)
    1. Required:
      1. Application-Name: My Applet Name
      2.  Permissions: sand-box/all-permission
    2. Optional:
      1. Codebase: {list of URLs}
      2. Trusted-Only: true (more secure)
      3. Trusted-Library: true (less secure than Trusted-Only)
      4. Application-Library-Allowable-Codebase: {list of URLs}
      5.  Caller-Allowable-Codebase: {list of URLs}
    3. {list of URLs}:
      1. Can be a single asterix as a wildcard '*' for liberal use of the JAR
      2. Can contain a mix of named/IP addresses
      3. Single line, no linebreaks
      4. Example: http://localhost:9090 https://localhost:9443 some-other.internal.url 172.16.0.1 
  • Signing (required)
    • Can be self-signed
    • Should not be expired
    • Within a secure intranet environment, it is possible to establish the following:
      • A self-signed certificate identified as a Root Certificate Authority (Root CA);
      • This certificate is added to all workstations within the network;
      • Generate a separate certificate for this application JAR file;
      • Sign the application certificate using the Root CA (e.g. 1 year expiry);
      •  Of course, you must be acquainted with the risks of being vulnerable to the CA certificate ever being compromised.
  • Workarounds:
    • Exception Site List
      • Add your URLs manually into individual workstation JRE Control Panel
    • Deployment Rule Set
      • Create an XML according to this page;
      • Better granular control over specific JRE versions you want your application to run using;
      • Wrap the XML inside a signed JAR (e.g. using above Root CA);
      • Push JAR into all workstations on startup.
With some luck using the above, you should not need to edit the java.policy where you have to end up exposing AllPermissions unwittingly causing a vulnerability you never intended to have.

Thursday, March 12, 2015

Generating archives on-the-fly in server memory

TL;DR - String byte array > text file > ZipEntry > ZipOutput > ByteArrayOutput > ServletOutput.

Looking into generating a jar file (essentially a zip archive), with a single text file within, that contains dynamically generated data, I located this and this which I'm attempting to combine for use.

The first part is straightforward; generating the content into the zip file as a ZipEntry with an explicit file name, all done in memory.

        try {
            String contents = "SomeStringA=SomeStringB";
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ZipOutputStream zos = new ZipOutputStream(baos);
            ZipEntry entry = new ZipEntry("file.txt");
            zos.putNextEntry(entry);            zos.write(contents.getBytes(StandardCharsets.UTF_8));//or just "UTF-8" if you're on Java 6
            zos.closeEntry();
            zos.close();//The archive needs to know it is done
        } catch (IOException e) {
            e.printStackTrace();
        }

The second part, I'm still mucking around a bit. This snippet should work... but I have yet to test this out. I'll update again once it is confirmed.

         response.setContentType("application/zip");         response.setHeader("Content-Disposition", "attachment; filename=\"file.jar\"");
         response.getOutputStream().write(baos.toByteArray());
         response.getOutputStream().flush();

Of course, all this is going to be done in a servlet method the is expecting a response output, to which the zip file will be churned out to (as a JAR file). Don't forget to flush()!

Edit: After running through the actual stuff, I realise that the ZipOutputStream needs to be closed as the final action. The code snippet above is pretty much spot on otherwise.

Friday, March 6, 2015

Accessing resource files within JARs

There were two items I needed to access:
  1. A keystore (.jks) file that contains a third-party certificate;
  2. A text file within a separate JAR file added as a library as part of the WAR package for the project.
Accessing the keystore file
The resources I'd found (like this one), all point to the same place. Using the calling class, reference a relative path to where the resource is located:

getClass().getResourceAsStream("../resources/keystore.jks");

This will work as long it is according to the Java package structure. In order to better understand where my class is calling from exactly, I'd output this to the server log:

getClass().getResourceAsStream(".");
This will show you the full absolute path to the Java class file you've been working with, on the server.

The JKS file can then be accessed like this:
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream keyStoreFile = getClass().getResourceAsStream(keyStorePath);
keyStore.load(keyStoreFile, keyStorePassword.toCharArray());

Accessing the text file in a JAR
This resource is slightly trickier. Mostly because it is contained within a separate JAR file. But don't worry, there is no need to use JarFile for this. My project was already using Maven, and has a pom.xml for packaging. But because this JAR does not contain any Java source files, it doesn't belong in the repository. I had to manually add it to the WAR file classpath by modifying the pom.xml file.

I added this snippet to the section meant for maven-war-plugin:
<archive>
  <manifest>
    <addClasspath>true</addClasspath>
    <classpathPrefix>lib/</classpathPrefix>
  </manifest>
  <manifestEntries>
    <Class-Path>lib/target.jar</Class-Path>
  </manifestEntries>
</archive>

Separately, in the calling Java class, note that the earlier getClass().getResourceAsStream("."); does not work in this instance. This is because the class is in a different JAR file. Instead, according to this, you need to make use of its ClassLoader like so:

getClass().getClassLoader().getResourceAsStream("file.txt"));

Note that file.txt is in the root of the target.jar file, but because target.jar has already been added to the WAR classpath, its contents is immediately visible to the ClassLoader of the same WAR file.

You can then read it like any normal text file:

BufferedReader br = new BufferedReader(new InputStreamReader(getClass().getClassLoader().getResourceAsStream(path)));

That is all. Have fun!

Thursday, February 26, 2015

Webservice over SSL

I've been working on an application that simulates a webservice call from a third-party application to our system. Initially a plain WAR running in vanilla Jetty/Tomcat, we eventually migrated it into WebSphere, the same platform we've been working on for the rest of the project. I didn't want to deal with the intricacies of SSL in Tomcat when it'd be much easier to manage in AS7... or so I'd thought.

Everything else was fine and dandy until I hit bits of error with messages in the log:

SRVE0068E: Uncaught exception created in one of the service methods of the servlet 

Caused by: javax.net.ssl.SSLHandshakeException: General SSLEngine problem

Caused by: com.ibm.jsse2.util.g: PKIX path building failed: java.security.cert.CertPathBuilderException: PKIXCertPathBuilderImpl could not build a valid CertPath.; internal cause is:
    java.security.cert.CertPathValidatorException: The certificate issued by XXXXXXX is not trusted; internal cause is:
    java.security.cert.CertPathValidatorException: Certificate chaining error


I found this solution and poked around in the WebSphere Admin Console. It was a straightforward task of instructing WebSphere to contact the webservice host (even if in my case, it was actually itself) in order to retrieve the SSL information in the form of a "handshake".

This simple step closed the loop. My original assumption was that WebSphere would use the SSL certificate from the same keystore. This was incorrect. The keystore used by the application server as a host is different from the truststore that it uses when it performs the role of a webservice client.

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!