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

Tuesday, 11 August 2015

Brute forcing my way Javascript, CSS etc

This is not a topic which will promote good design pattern or coding style. Instead this is about getting something done.

 

Detecting some change in page with Javascript


Many a times I need to get the size of an HTML element which is loaded dynamically through Javascript and do something as soon as it has changed it's size. Due to not understanding the code or poor design of it I have to brute force my way. To do it, I simply repeat the a timed function till the event occurs. Take a look at the following code:
var maxLoopTime = 4000;//milli seconds
var startTime = new Date().getTime();
var myInterval = setInterval(function () {
 try {
  var timeExceeded = new Date().getTime() - startTime > maxLoopTime;
  var detected = isChangeDetected();
  
  if(timeExceeded || detected) {
   clearInterval(myInterval);
   doSomething();
  }
 } catch(err) {
  clearInterval(myInterval);
  doSomething();
 }
}, 300);

function isChangeDetected () {
 //check for the condition
}

function doSomething () {
 //do something once even occurs
} 
isChangeDetected looks for the change, in my case it's the height change of an HTML element.
doSomething is the function which I want to call when the HTML element has taken the final size.

CSS in a dynamically created element:

Sometimes when loading some HTML elements dynamically we need to apply some properties to other part of the web page or to the element itself.
One way of doing it is to monitor the page waiting for the element to appear. I don't know how I would achieve it, may be by using some third party library.
However how about simplest brute solutions below:
  1. Add the CSS <style> element as the part of the dynamically loaded HTML element. As soon as the element is loaded it web page will apply the style and when it's unloaded page will again revert the changes.
  2. Add a Javascript <script> tag to this element
    <div id="myElement">
        <style>
            body: {
                background-color: red;
            }
        </style&gt
        <!-- or !-->
        <script>
                console.log("finally loaded");
        </script>
    </div>
Off course in Case 1 you have to make sure that when element unloads it's deleted entirely otherwise effects of the <style> tag will remain even after the dynamically loaded element has disappeared (visibility: none or display:none).
By adding the <style> or <script> tag to the element here means is adding these tags to the template or the html content of the loaded element.

I'm fully aware of the fact that both of these actions will lead to a reflow and redrawing of the DOM and webpage, that's why title here is brute force.

Detecting Flash Plugin with JavaScript

It's nothing fancy. It certainly will not work for Internet Explorer's old versions.
I simply needed something to disable certain functionality if Flash was not installed on Tablets/iPads.
If you need to detect an iPad or you're looking for a robust plugin to detect Flash you're better off using media queries or some JavaScript Flash library.

Here it is:

CoffeeScript version

isFlashInstalled = ->
      try
        flashPlugin = _.filter(navigator.plugins, (el) -> return el.description.match(/.*shockwave flash*/i))
        flashPlugin != null && flashPlugin.length != 0
      catch err
        console.log("error in detecting flash")
      flashPlugin != null and flashPlugin.length != 0

JavaScript translated version

var isFlashInstalled = function() {

        var err, flashPlugin;
        try {
          flashPlugin = _.filter(navigator.plugins, function(el) {
            return el.description.match(/.*shockwave flash*/i);
          });
          flashPlugin !== null && flashPlugin.length !== 0;
        } catch (_error) {
          err = _error;
          console.log("error in detecting flash");
        }
        return flashPlugin !== null && flashPlugin.length !== 0;
      }

Here I'm using underscore js for the array filtering, you can off course use a plain for loop or array forEach method.