Thursday, 24 September 2015

Keyboard shortcuts and mouse functions for HTML 5 video

This time I was playing with HTML 5 video tag, but there are no keyboard shortcuts out of the box.
So I implemented a simple jQuery plugin.
Please find the link below:
https://github.com/ConsciousObserver/Html5VideoShortCutPlugin/blob/master/public/js/plugin/Html5VideoShortCutPlugin.js

I have implemented a few shortcuts which I use in VLC media player. Code can be easily extended without much fuss.
As of now following options are supported:

  1. Play/pause (spacebar and mouse click)
  2. Video navigation (10 seconds, 25 seconds, 40 seconds, left right arrows with ctrl or shift)
  3. Volume control (up and down arrows)
  4. Fast forward (+ key)
  5. Slow down (- key)
  6. Screenshot (shift + e)
Usage details can be found below:
https://github.com/ConsciousObserver/Html5VideoShortCutPlugin

Following is a working example:

Due to blogger trapping many events some shortcuts may not work here as expected, however you can use 'f' or press 'enter' to enter fullscreen mode. After that all shortcuts should work.

Friday, 18 September 2015

Bundle_xyz.jar does not have a META-INF/MANIFEST.MF! Make sure, META-INF and MANIFEST.MF are the first 2 entries in your JAR!

I encountered this during deployment of an OSGI bundle on Karaf.
I needed to change some configuration files in the bundle JAR file, so instead of rebuilding it, I just unpacked it on the remote server, modified the configuration and then archived it using ZIP tool.
After all, that's what I thought a JAR file was, a ZIP file. I had followed the same process hundreds of times on my local setup and had never gotten the error. I thought something was wrong with the server. I thought the user with which I had deployed the JAR and the user with which server process was running were two different users, and since server process did not have the access to the JAR deployed by me, it couldn't read it and hence the error. A well formed possible explanation to make the sense of the things again and leave the burden to someone else.
After a lot of searching and reading I found what was wrong.

JAR file is not a simple ZIP file.

When creating a ZIP file any tool/program simply follows it's own guideline for which entries should be made first in the archive file. Most of the times it would be in alphabetic order where directories appear first.
However in a valid JAR file order of entries is not as loosely defined as in a ZIP file. In a valid JAR file following should be the first 2 entries:

  1. META-INF/
  2. META-INF/MANIFEST.MF
After these 2 entries rest of entries can be in any order.
Entries in a JAR file can be printed using following command :
jar -tf Test.jar

For example running above command on commons-lang-2.6.jar prints following output (I have included only top few lines)

META-INF/
META-INF/MANIFEST.MF
org/
org/apache/
org/apache/commons/
org/apache/commons/lang/
org/apache/commons/lang/builder/
org/apache/commons/lang/enum/
org/apache/commons/lang/enums/
org/apache/commons/lang/exception/
org/apache/commons/lang/math/
org/apache/commons/lang/mutable/
org/apache/commons/lang/reflect/
org/apache/commons/lang/text/
org/apache/commons/lang/time/
META-INF/LICENSE.txt
META-INF/NOTICE.txt

As you can see first two entries are META-INF/ and META-INF/MANIFEST.MF.

After getting this information, solution is obvious, just build the JAR using the build process that you are using and then deploy it on server.

This rule of first two fixed entries is ignored by many containers but we never know which one will enforce it. Safest way is to manipulate JAR file using the tools provided by the JDK.
The same rule seems to be true for WAR file also, but I have yet to encounter an application server which enforces it.

...till next time

Wednesday, 16 September 2015

Spring OSGI bundle, how does Karaf know where to get the bean configuration files

I have been working with some OSGI based applications and they are deployed on the OSGI container Karaf. I know I should have looked it up earlier however I recently realized I did not know how the Karaf knew where it could find my bean configuration files. I looked around and found out the answer.
Quick answer is below and for more keep reading.
Karaf looks at two places to get the clue of where the bean configuration files are :

  1. META-INF/MANIFEST.MF (if Spring-Context attribute is present)
  2. META-INF/spring (if it contains XML files)
To be able to load an Spring application Karaf needs a feature called Spring Dynamic Modules or in short Spring DM. To query the list of the features you can run

features:list

Or some combination of the same command (I'm using Talend Karaf runtime).

Spring-DM feature loads following bundles
  • org.springframeork.osgi.bundle.extender
  • org.springframeork.osgi.bundle.core
  • org.springframeork.osgi.bundle.io
org.springframeork.osgi.bundle.extender keeps on querying the OSGI container for the newly loaded bundles to see if they are powered by Spring. It checks first the Spring-Context attribute in META-INF/MANIFEST.MF if not present it checks the META-INF/spring for XML files.
When bundles containing the configuration files are found, it simply initializes the Spring Context.

I found this information on an old entry on Javaworld
Introduction to Spring Dynamic Modules

Though old information here is still relevant and true. Spring DM in all likelihood is probably dead and it is succeeded by Eclipse Gemini Blueprint.
Follow the links for more info :
Spring Dynamic Modules becomes Eclipse Gemini Blueprint
relationship between Spring Dynamic Modules and Gemini Blueprint

However even after all this time Spring-DM can be seen at work in many places and it still works fine out of the box.

Tuesday, 15 September 2015

Foolproof method of excluding Resteasy API provided byJBoss 6 EAP or JBoss 7 AS from application

A few months back I was working on a REST based web service application using CXF. CXF provides a cool XML file out of box describing the methods available in the REST interfaces. Since I was using CXF I did not want JBoss scanning my REST interfaces and interfering with the classloader of my application. That's right class loader of the application (WAR, EAR). I'm taking a detour to explain what I meant there. There's a hierarchy of classloaders in JVM. In general if an application requires a class it asks it's classloader, this classloader asks it's parent classloader first. This way it's made sure that each class is uniquely available to entire JVM as the resolution is done by the parent classloaders first. It's an object hierarchy not a class hierarchy (not parent child, it's just a list). However, in case of servlet containers or JavaEE containers, applications try to load the class themselves first, instead of asking for parent first. This was done so that web application is run with the versions of the classess that were packed with it.

 Reference : Do You Really Get Class Loaders? A video from the ZeroTurnaround, creators or JRebel

Coming back to the topic, since there were two different implementations of REST API (resteasy and CXF) available to my application, there was a conflict. No matter what I did, I could not get the JBoss to serve the WADL document. I was sure that this was happening because of the interference of Resteasy with the CXF. I looked around and couldn't find the foolproof method. Most were asking to exclude Resteasy, JAXRS subsystems in jboss-deployment-structure.xml file. Well I did that and that didn't work. I thought may be it couldn't solve my problem because I was trying to force solutions for the old JBoss version on the new one. However the problem runs deeper.

JBoss 6 has a maven like module system (just maven like, dependencies and all in "modules" directory). Every web application has a dependency on JAVAEE module, which in turn loads Resteasy and what not as dependency. (modules/javaee/api/main/module.xml)

I could'nt exlude JAVAEE module, or could I? It turned out that I could actually exclude JAVAEE entirely, and as JAVAEE module itself is just a name for a collection of dependency modules, I could now load these modules selectively. Take a look at modules/javaee/api/main/module.xml to see what dependencies are loaded by JAVAEE module. If you try this method, it's guaranteed that this will cause less suffering and pain than any other method out there. Just start with everything(dependencies) excluded, except for the ones you know that no Java application can run without and start adding dependencies by taking the hint from the error that JBoss gives you during the deployment. I'm copying a sample jboss-deployment-structure.xml file here. It should be packed in the WEB-INF directory of the web application.

And yes, this solved my problem, finally I was able to get the CXF provided WADL document.
As you can see I have excluded parts which even remotely whisper web service.

Sample WEB-INF/jboss-deployment-structure.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
  <deployment>
    <exclude-subsystems>
      <subsystem name="jaxrs" />
      <subsystem name="webservices" />
      <subsystem name="resteasy" />
    </exclude-subsystems>
    <exclusions>
      <module name="javaee.api" />
      <module name="javax.ws.rs.api" />
      <module name="org.jboss.as.jaxrs" />
      <module name="org.jboss.resteasy.resteasy-jaxrs" />
      <module name="org.jboss.resteasy.resteasy-cdi" />
      <module name="org.jboss.resteasy.jackson-provider" />
      <module name="org.jboss.resteasy.resteasy-atom-provider" />
      <module name="org.jboss.resteasy.resteasy-hibernatevalidator-provider" />
      <module name="org.jboss.resteasy.resteasy-jaxb-provider" />
      <module name="org.jboss.resteasy.resteasy-jettison-provider" />
      <module name="org.jboss.resteasy.resteasy-jsapi" />
      <module name="org.jboss.resteasy.resteasy-multipart-provider" />
      <module name="org.jboss.resteasy.resteasy-yaml-provider" />
      <module name="org.codehaus.jackson.jackson-core-asl" />
      <module name="org.codehaus.jackson.jackson-jaxrs" />
      <module name="org.codehaus.jackson.jackson-mapper-asl" />
      <module name="org.codehaus.jackson.jackson-xc" />
      <module name="org.codehaus.jettison" />
      <module name="org.jboss.as.webservices.*" />
      <module name="org.jboss.ws.*" />
    </exclusions>

    <dependencies>
      <module name="javax.activation.api" export="true" />
      <module name="javax.annotation.api" export="true" />
      <!-- <module name="javax.ejb.api" export="true" />
      <module name="javax.el.api" export="true" /> -->
      <module name="javax.enterprise.api" export="true" />
      <module name="javax.enterprise.deploy.api" export="true" />
      <module name="javax.inject.api" export="true" />
      <module name="javax.interceptor.api" export="true" />
      <!-- <module name="javax.jms.api" export="true" />
      <module name="javax.jws.api" export="true" />
      <module name="javax.mail.api" export="true" />
      <module name="javax.management.j2ee.api" export="true" /> -->
      <module name="javax.persistence.api" export="true" />
      <module name="javax.resource.api" export="true" />
      <!-- <module name="javax.rmi.api" export="true" />
      <module name="javax.security.auth.message.api" export="true" />
      <module name="javax.security.jacc.api" export="true" /> -->
      <module name="javax.servlet.api" export="true" />
      <module name="javax.servlet.jsp.api" export="true" />
      <module name="javax.transaction.api" export="true" />
      <module name="javax.validation.api" export="true" />
      
      <!-- <module name="javax.ws.rs.api" export="true" services="export" /> -->
      
      <module name="javax.xml.bind.api" export="true" />
      <module name="javax.xml.registry.api" export="true" />
      <module name="javax.xml.soap.api" export="true" />
      <module name="javax.xml.ws.api" export="true" />

      <!-- This one always goes last. -->
      <module name="javax.api" export="true" />
    </dependencies>

  </deployment>
</jboss-deployment-structure>

In this application I was loading my own Jackson library, so that's also excluded.
...till next time