Friday, October 25, 2013

JSF 2: Returning a resource URL that is local to the web application

JSF 2 introduced a standard way of serving website assets - like JavaScript, CSS, image files, etc. It also introduced features which facilitate the insertion of these resources in to a JSF page.

Consider the following web application code base layout:



This application, as you can see, uses the Bootstrap framework, which requires JQuery as a prerequisite. With the new resource loading solution, you can include the above files in your XHTML template by adding the following declarations:


   <h:head>
      <h:outputStylesheet library="bootstrap" name="css/bootstrap.css"/>
      <h:outputStylesheet library="bootstrap"
                          name="css/bootstrap-theme.css"/>
      <h:outputScript library="jquery" name="js/jquery.min.js"/>
      <h:outputScript library="bootstrap" name="js/bootstrap.min.js"/>
    </h:head>


Notice in the above declarations, there is no version number provided for the JQuery script, and in the code base, we are using '_'s instead of '.'s to separate the version numbers. In JSF 2, the ResourceHandler takes care of identifying the latest version of your Resource and renders the appropriate file. This is an extremely useful feature, where we can update our resources and JSF will ensure that the latest version is the artefact that is rendered.

Everything works fine, with the exception of on small glitch! Relative references inside your CSS file do not work! This has to do with the way the resources are rendered. If you are using JSF with the suffix mapping (the typical scenario where you say that all .xhtml file are to be processed by JSF), then the above declarations would produce the following output:


    /javax.faces.resource/css/bootstrap.min.css.xhtml?ln=bootstrap
    /javax.faces.resource/js/jquery.js.xhtml?ln=jquery&v=1_10_2

, so if the bootstrap.min.css file had a relative reference to the fonts file, via the following declaration:

    url('../fonts/glyphicons-halflings-regular.eot');

The URL that gets constructed does not make a lot of sense

    /javax.faces.resource/fonts/glyphicons-halflings-regular.eot

As you can see, the above URL does not exist, and since there is no .xhtml file at the end of the URL the Faces servlet doesn't handle it either.

Now there are 3 ways of handling this issue:

  1. Don't use the JSF resource loading mechanics. Instead include the web resources by constructing the URL directly

    <link rel="stylesheet" type="text/css"
          href="#{request.contextPath}/resources/bootstrap/css/bootstrap.min.css"/>
     
  2. Modify the CSS file and update all the relative URLs to be JSF aware

    url('../fonts/glyphicons-halflings-regular.eot.xhtml?ln=bootstrap');
  3. Or, you can write a custom ResourceHandler that will:
    • Allow you to use external web assets without making any changes to them.
    • Leverage the JSF resource loading mechanics and all the goodness that comes along with it.
    • Generate a URL that looks like option 1.

The Custom ResourceHandler


One really good feature about JSF is that you can replace / augment pretty much any part of the framework. In order to achieve our use case, we will define a new ResourceHandler and an associated Resource type.



The AppResourceHandler extends the ResourceHandlerWrapper, and monitors only those method calls that create a resource object.

For every resource object that gets created, the URL of the resource is analyzed to determine if that resource is local to the webapp or part of some archive (jar / zip / etc.) file in the class path.

AppResourceHandler.java
...

@Override
public Resource createResource(String resourceName, String libraryName) {
    Resource resource = super.createResource(resourceName, libraryName)
    return getWrappedResource(resource);
}

/**
 * If the given resource object can be rendered locally, then do so by
 * returning a wrapped object, otherwise return the input as is.
 */
private Resource getWrappedResource(Resource resource) {
    WebAppResource webAppResource = null;
    ExternalContext context = FacesContext.getCurrentInstance()
                                          .getExternalContext();
    // Get hold of the webapp resources directory name
    if (resourcesRoot == null) {
        String resourcesRoot = context.getInitParameter(
                ResourceHandler.WEBAPP_RESOURCES_DIRECTORY_PARAM_NAME);
        if (resourcesRoot == null) {
            resourcesRoot = "/resources";
        }
    }

    if (resource != null) {
        URL baseURL = resource.getURL();
        if (baseURL != null) {
            String extForm = baseURL.toExternalForm();
            int idx = extForm.indexOf(resourcesRoot);
            if (idx != -1) {
                try {
                    extForm = extForm.substring(idx);
                    URL resourceURL = context.getResource(extForm);
                    if (resourceURL != null) {
                        webAppResource = new WebAppResource(extForm,
                                                            resource);
                    }
                } catch (MalformedURLException e) {}
            }
        }
    }
    return webAppResource != null ? webAppResource : resource;
}

And in the WebAppResource class, we just return the webapp local URL instead of a Faces one.

WebAppResource.java
...
private Resource wrapped;
private String path;

public WebAppResource(String path, Resource wrapped)
{
    this.wrapped = wrapped;
    this.path = path;
}

@Override
public String getRequestPath()
{
    FacesContext context = FacesContext.getCurrentInstance();
    return context.getApplication().getViewHandler()
                  .getResourceURL(context, path);
}

Finally, we add the custom resource handler's entry in the faces-config.xml file.

<faces-config ...>
    <application>
        <resource-handler>
            some.package.AppResourceHandler
        </resource-handler>
    </application>
</faces-config>


And we are done. Run your code and see the difference!

Hope this was useful to you.

Monday, October 21, 2013

ADF Mobile: Working with iframes

Oracle ADF Mobile, as of version 11.1.2.4.0, does not provide a web browser control that you can embed and use. There is the notion of implementing an application feature as a remote url, but that is tied in to a connection that cannot be modified at runtime.

I happened to have a need for modifying the context parameters of the target location at runtime.

Here is what I was trying to do:
  1. Fetch some data using a REST service
  2. On a certain action, show the web site inside the application itself
For the second operation, all I wanted to do was construct the target URL and display it in an IFRAME. Turns out there is no easy way to do it!

So I ended up developing a hack that looks like this:



The App Browser Proxy

I added a new feature - "App Browser Proxy" - whose sole purpose was to display a URL (which has been white listed). Since remote connections are out of the question, I decided to implement this feature as a Local HTML file instead.

The good thing about implementing a feature as a local HTML file is that you can add HTML elements like scripts & iframes. The only problem is how do you pass data from your business controller to the page?

The developer guide talks about how one can use JavaScript to get hold of the business logic code, but it is classified under "Using ADF Mobile APIs to Create a Custom HTML Springboard Application Feature". Not exactly the set of keywords I am looking for, but nonetheless in here lies the solution for this hack.

Putting these concepts together, yielded the following code for my local HTML file:


/Web Content/app-browser-proxy/pages/app-browser-proxy.xhtml
<script type="text/javascript">
  if (!window.adf) { window.adf = {}; }
  adf.wwwPath = "../../../../../www/";
</script>
<!-- Take note about the number of folders that you need to skip -->
<script type="text/javascript" src="../../../../../www/js/base.js"></script>
<script type="text/javascript" src="../../../../../www/js/adf.el.js"></script>
<script type="text/javascript">
  function onEvaluateSuccess(req, res) {
    try {
      if (res) {
        if (res[0]) {
          var targetURL = res[0].value;
          if (targetURL) {
            var ifrm = document.getElementById("app-browser-iframe");
            if (ifrm) {
              ifrm.src = targetURL;
            }
          }
        }
      }
    }
    catch (e) {
      alert("Load Target: error occurred " + e);
    }
  }

  function onError(req, resp) {
    alert("Error occurred " + adf.mf.util.stringify(res));
  }

  function loadTargetURL() {
    try {
      adf.mf.el.getValue("#{applicationScope.appBrowserTargetUrl}",
                          onEvaluateSuccess, onError);
    }
    catch (e) {
      alert("Caught exception: " + e);
    }
  }
  document.addEventListener("showpagecomplete", loadTargetURL, false);
</script>
<iframe id="app-browser-iframe" width="100%" height="100%"
           style="background-color:#ffffff; border: none;"></iframe>


In the above fragment, I have achieved the following:
  1. Create an IFRAME that occupies the entire height & width of the screen
  2. Using JavaScript, fetch the target URL by evaluating an EL - applicationScope.appBrowserTargetUrl
  3. Set the IFRAME's source to the evaluated EL

The App Browser Proxy Feature Listener

The thing about ADF Mobile is that once it loads a feature, it does not necessarily reload it every time you navigate between features. So, the above code will work perfectly for the first time. Second time you navigate to this page, you will continue to see the last loaded page.

In order to work around this issue, you need to create a Feature Listener, that invokes the JavaScript function that we have defined - loadTargetURL().

In the activate method of the feature listener, adding the following snippet will do the trick:

AdfmfContainerUtilities.invokeContainerJavaScriptFunction(
      AdfmfJavaUtilities.getFeatureName(), "loadTargetURL",new Object[0]);


The Final Piece

Finally, invoking this app browser proxy, from any other feature, is a matter of adding the following code snippet:

// Ensure that the target URL has been set in the following EL
// #{applicationScope.appBrowserTargetUrl}
// e.g.,
// AdfmfJavaUtilities.setELValue(
//       "#{applicationScope.appBrowserTargetUrl}", targetURL)

// Use the correct feature ID
AdfmfContainerUtilities.resetFeature("demo.AppBrowserProxy");
AdfmfContainerUtilities.gotoFeature("demo.AppBrowserProxy");

Note: This hack works because I store the target URL in the application scope. Any other scope will not be available across features.

Hope this was useful to you.

Thursday, October 17, 2013

ADF Mobile: Listening for changes to preferences

Lately I've been doing quite a bit of prototyping and, as is the case with such activities, I kept coming up with weird requirements that didn't have any apparent answers available .... some can be solved with a bit of ingenuity, the others leave me a few hair strands less.

Oracle ADF Mobile is a pretty neat framework for designing cross platform mobile apps. I was tasked with creating a prototype that communicates with a REST service to fetch some data.

As I went about with the exercise, I decided that I wanted to store the server configuration and user credentials in the application preferences.


Done. Next I wanted to refresh my controllers every time the preferences changed ... well this is where I got stumped! There is no preference-change-listener that I could implement.

Fortunately, there is an alternative - a feature level LifeCycleListener - which has only two methods activate() & deactivate() which are invoked when the feature is displayed and hidden. Every time you access the app's settings page (on your android phone) you are forcing the "Feature" to "deactivate" and then "activate" when you come back.

It is in this listener that you could employ a nifty hack to check if your preferences have changed or not. I chose to verify the hash codes to determine if the preferences had changed; and accordingly invalidate the required content.

So I ended with the following code for my listener:


public class PortalFeatureListener
  implements LifeCycleListener
{
  private long prefsHashCode = -1;

  public void activate()
  {
    // Verify if the hashcode of the preferences has changed or not.
    String prefs = "" +
     AdfmfJavaUtilities.evaluateELExpression(
        "#{preferenceScope.application.serverConfig.connUrl}") +
     AdfmfJavaUtilities.evaluateELExpression(
        "#{preferenceScope.application.userCredentials.username}") +
     AdfmfJavaUtilities.evaluateELExpression(
        "#{preferenceScope.application.userCredentials.password}");
    
    if(prefs.hashCode() != prefsHashCode) 
    {
      prefsHashCode = prefs.hashCode();
      refreshControllers();
    }
  }

  public void deactivate()
  {
    // Do nothing for now.
  }
  
  private void refreshControllers() 
  {
    // Do something here.
  }
}


, which gets configured in the adfmf-feature.xml file.



Hope this was useful to you.


Tuesday, October 08, 2013

Getting WELD working on top of GAE/J

I have a pet project that I keep tinkering with whenever I get some free time. It is an elaborate experiment to build what I wish to call as the "Unified Model Layer". More details about it in another post. As with any experiment there are a lot of observations and learnings. One such learning was on how to get WELD running on top of Google App Engine.

Remember - an App Engine app may not spawn new threads.

Once you understand this rule, then all you need to do is dig through any library code that might want to spawn threads and disable it.

In the case of WELD, you need to create 2 property files, with the associated content:

org.jboss.weld.bootstrap.properties

  # Custom overrides to make weld work in the GAE environment.
  concurrentDeployment=false
  preloaderThreadPoolSize=0


org.jboss.weld.executor.properties

  # Executors should not be created on the GAE environment.
  threadPoolType=NONE

Ensure that these property files end up in your WEB-INF/classes directory. That will ensure that WELD does not spawn new threads at application startup.

Hope this was useful to you.

Monday, October 07, 2013

The Empty JSF Component

JSF 2 was the much needed improvement on top of its predecessor. Most notable of its features was the Facelets view declaration language which, amongst various other things, allowed mixing HTML and JSF components together in the same file. A direct impact of this capability, was the ease with which a developer could translate HTML files in to functioning XHTML code.

But then again, every now and then there would be this case where you need to set a rendered condition for a HTML element - like menu items or some thing represented using a <li> tag - which does not have any equivalent component in JSF. The most common way of handling such a situation is via the <c:if/> tags. Not a very elegant looking solution, but it works.

Now if you, like me, are not a big fan of using the JSTL functions, then the chances are that you might have wondered, "Why doesn't standard JSF just provide an empty JSF component that just had one attribute and only one attribute - a rendered condition!"

Well, fret not, things are looking brighter. In the latest release, JSF 2.2, the FacesComponent annotation got a make over with the addition of 3 new attributes:
  • createTag - If set to true the component will be directly useable via a tag on a Facelet. 
  • tagName - Optional explicit name for the tag. If omitted, the class’ simple name with the first character in lowercase will be used. 
  • namespace - Optional explicit namespace for the tag. If omitted the namespace ‘http://xmlns.jcp.org/jsf/component’ will be used. 

, meaning you can create this empty JSF component with code as simple as the following:

@FacesComponent(value = "group", createTag = true, tagName = "group")
public class GroupComponent extends UIComponentBase
{
    @Override
    public String getFamily()
    {
        return "Rogue.IO";
    }
}

Next, you can use this new empty component in your Facelet page as shown below:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
                xmlns:r="http://xmlns.jcp.org/jsf/component">
    <!-- Adaptation of the bootstrap based drop down menu -->
    <r:group rendered="#{menu.has.children}">
        <li class="dropdown">
            <a href="#" class="dropdown-toggle"
                        data-toggle="dropdown">
                #{menu.name} <b class="caret"></b>
            </a>
            <ul class="dropdown-menu">
                <ui:repeat value="#{menu.children}" var="child">
                    <li>
                        <a href="#">#{child.name}</a>
                    </li>
                </ui:repeat>
            </ul>
        </li>
    </r:group>
    <r:group rendered="#{not menu.has.children}">
        <li>
            <a href="#">#{menu.name}</a>
        </li>
    </r:group>
</ui:composition>

No more JSTL hacks. Hope this was useful to you.