Sunday, July 29, 2018

OmniFaces 3.2 adds o:hashParam, CDNResource and UUID in exception logging

OmniFaces 3.2 has been released!

Next to a bunch of utility methods, this version adds a <o:hashParam> and a CDNResource, and the FullAjaxExceptionhandler and FacesExceptionFilter will from now log the exceptions with an UUID and user IP.

You can find the complete list of additions, changes and fixes at What's new in OmniFaces 3.2? list in showcase.

Installation

Non-Maven users: download OmniFaces 3.2 JAR and drop it in /WEB-INF/lib the usual way, replacing the older version if any.

Maven users: use <version>3.2</version>.

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces</artifactId>
    <version>3.2</version>
</dependency>

o:hashParam

This new brother of <f|o:viewParam> will less or more do the same things, but then with hash query string parameters instead of request query string parameters.

The "hash query string" is the part in URL after the # which could be formatted in the same format as a regular request query string (the part in URL after the ?). An example:

http://example.com/page.xhtml#foo=baz&bar=kaz

This specific part of the URL (also called hash fragment identifier) is by default not sent to the server. The <o:hashParam> will on page load and on every window.onhashchange event send it anyway so that the JSF model gets updated, and on every JSF ajax request update the hash query string when the corresponding JSF model value has changed.

Check out the live demo!

CDNResource

This is a new javax.faces.application.Resource subclass which can be used by your custom ResourceHandler implementation which automatically uploads the local resources to a CDN and then returns the CDN URL instead of the local URL. The CDNResource offers the CombinedResourceHandler the opportunity to automatically generate a fallback URL to the local resource into the <script> and <link rel="stylesheet"> elements generated by the associated <h:outputScript>, <o:deferredScript> and <h:outputStylesheet> components. This is for now indeed only useful when you have enabled the CombinedResourceHandler. A more general appliance may come in a future OmniFaces version which should also cover non-combined resources and images.

Imagine that you have the below custom resource handler configured as <resource-handler> in faces-config.xml which automatically uploads all local CSS/JS/image resources to a Amazon S3 based CDN:

public class AmazonS3ResourceHandler extends DefaultResourceHandler {

    @Inject
    private AmazonS3Service s3;

    public AmazonS3ResourceHandler(ResourceHandler wrapped) {
        super(wrapped);
    }

    @Override
    public Resource decorateResource(Resource resource, String resourceName, String libraryName) {
        if (resource != null && Utils.endsWithOneOf(resourceName, ".js", ".css", ".jpg", ".gif", ".png", ".svg")) {
            return new CDNResource(resource, s3.getURL(resource, resourceName, libraryName));
        }
        else {
            return resource;
        }
    }
}

Whereby the your custom AmazonS3Service looks something like this, using the com.amazonaws:aws-java-sdk-s3 library:

@ApplicationScoped
public class AmazonS3Service {

    // key = resource path, value = last modified timestamp
    private static final Map<String, Long> RESOURCES = new ConcurrentHashMap<>();

    private AmazonS3 client;
    private String bucket;
    private String baseURL;

    @PostConstruct
    private void init() {
        client = AmazonS3ClientBuilder.standard().build(); // Use your own builder of course.
        bucket = "cdn"; // Use your own S3 bucket name of course.
        baseURL = "https://cdn.example.com/"; // Use your own CDN URL of course.
    }

    public String getURL(Resource resource, String resourceName, String libraryName) {
        String path = Paths.get(Utils.coalesce(libraryName, "")).resolve(resourceName).toString();
        Long lastModified = RESOURCES.computeIfAbsent(path, k -> uploadIfNecessary(resource, path));
        return baseURL + path + "?v=" + lastModified;
    }

    private long uploadIfNecessary(Resource resource, String path) {
        Long s3LastModified = null;
        long localLastModified;

        try {
            if (resource instanceof DynamicResource) { // E.g. combined resource.
                localLastModified = ((DynamicResource) resource).getLastModified();
            }
            else {
                localLastModified = resource.getURL().openConnection().getLastModified();
            }

            if (client.doesObjectExist(bucket, path)) {
                s3LastModified = client.getObjectMetadata(bucket, path).getLastModified().getTime();

                if (localLastModified > s3LastModified) {
                    s3LastModified = null;
                }
            }

            if (s3LastModified == null) {
                s3LastModified = localLastModified;
                upload(resource, path, s3LastModified);
            }
        }
        catch (Exception e) {
            throw new FacesException(e);
        }

        return s3LastModified;
    }

    private void upload(Resource resource, String path, long lastModified) throws Exception {
        String filename = Paths.get(path).getFileName().toString();
        byte[] content = Utils.toByteArray(resource.getInputStream());

        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentType(resource.getContentType());
        metadata.setContentLength(content.length);
        metadata.setContentDisposition(Servlets.formatContentDispositionHeader(filename, false));
        metadata.setLastModified(new Date(lastModified));

        client.putObject(bucket, path, new ByteArrayInputStream(content), metadata);
    }

}

Then the CDNResource marker class will automatically force the CombinedResourceHandler to generate the following <script> and <link rel="stylesheet"> markup:

<script type="text/javascript" src="https://cdn.example.com/omnifaces.combined/XYZ.js?v=123"
    onerror="document.write('&lt;script src=&quot;/javax.faces.resource/XYZ.js.xhtml?ln=omnifaces.combined&v=123&quot;&gt;&lt;/script&gt;')"></script>

<link rel="stylesheet" type="text/css" href="https://cdn.example.com/omnifaces.combined/XYZ.css?v=123"
    onerror="this.onerror=null;this.href='/javax.faces.resource/XYZ.css.xhtml?ln=omnifaces.combined&v=123'" />

You see, the CombinedResourceHandler will automatically include the onerror attribute which points to the local URL as a fallback.

UUID and IP in exception logs

A very common requirement in real world projects is that any logging of the exception stack trace should also include an unique identifier (UUID) which in turn is also included in the error page and/or an automatic exception email to the administrator. This will make it easier to find back the stack trace in the server logs.

The OmniFaces FullAjaxExceptionHandler and FacesExceptionFilter will from now on also include the UUID and client IP address in the exception logs. Besides, the FacesExceptionFilter will now also start logging exception stack traces wheres it didn't do it. The UUID is in turn available as a request attribute with the name org.omnifaces.exception_uuid which you can if necessary easily include in your custom error page.

<li>Error ID: #{requestScope['org.omnifaces.exception_uuid']}</li>

These improvements will make it unnecessary to customize the FullAjaxExceptionHandler and/or FacesExceptionFilter to include the UUID.

How about OmniFaces 2.x and 1.1x?

The 2.x got on special request also the CDNResource and hence steps from 2.6.9 to 2.7 instead of to 2.6.10. For the remainder only bugfixes are done and no other new things. As long as you're still on JSF 2.2 with CDI, you can continue using latest 2.x.

The 1.1x is basically already since 2.5 in maintenance mode. I.e. only critical bugfix versions will be released. It's currently still at 1.14.1 (May 2017), featuring the same features as OmniFaces 2.4, but without any JSF 2.2 and CDI things and therefore compatible with CDI-less JSF 2.0/2.1.

Maven download stats

Here are the 2018's Maven download stats so far:

  • January 2018: 14646
  • February 2018: 14786
  • March 2018: 18059
  • April 2018: 16642
  • May 2018: 17876
  • June 2018: 17432
  • July 2018: TBD

The Definitive Guide to JSF in Java EE 8

Just in case if you have missed it .. I have finally written a book!

The Definitive Guide to JSF in Java EE 8 is since July 11, 2018 available at Amazon.com. This book is definitely a must read for anyone working with JSF or interested in JSF. It uncovers the history, inner workings, best practices and hidden gems of JSF. The source code of the book's examples can be found at GitHub.

Thursday, April 12, 2018

OmniFaces 3.1 adds MessagesKeywordResolver and SourceMapResourceHandler

OmniFaces 3.1 has been released!

Next to a bunch of utility methods, this version adds a MessagesKeywordResolver and a SourceMapResourceHandler. This version also deprecates WebXml.INSTANCE and FacesConfigXml.INSTANCE with as replacement the WebXml.instance() and FacesConfigXml.instance() respectively via an interface instead of an enum. This way they can easier be mocked during unit testing.

You can find the complete list of additions, changes and fixes at What's new in OmniFaces 3.1? list in showcase.

Installation

Non-Maven users: download OmniFaces 3.1 JAR and drop it in /WEB-INF/lib the usual way, replacing the older version if any.

Maven users: use <version>3.1</version>.

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces</artifactId>
    <version>3.1</version>
</dependency>

MessagesKeywordResolver

This is the first addition which utilizes a brand new JSF API: the Component Search Expression Framework. The MessagesKeywordResolver basically adds support for @messages in <f:ajax render>.

<h:inputText id="input1" ... />
<h:message id="m_input1" for="input1" />
<h:inputText id="input2" ... />
<h:message id="m_input2" for="input2" />
<h:inputText id="input3" ... />
<h:message id="m_input3" for="input3" />
...
<h:commandButton ... />
    <f:ajax execute="@form" render="@messages" />
</h:commandButton>

This way, only the message components are updated on submit. This is more efficient than e.g. render="@form" which unnecessarily also updates the input and button components.

SourceMapResourceHandler

If you've more than often debugged minified CSS/JS resources in browser developer toolset, you'll probably ever have heard of "source maps", usually referenced by a code comment somewhere in the bottom of the minified file, such as /*# sourceMappingURL=some.js.map */. To the point, these files represent the mapping between the minified version and the unminified version, so that the browser developer toolset can after downloading the source map file automatically unminify the minified version, whereby the original variable names are substituted back.

Very useful, particularly for minified/uglified JS resources. Unfortunately, resolving the source map files doesn't always work well with suffix-mapped JSF resources which uses a virtual URL pattern with /javax.faces.resource/ prefix and .xhtml suffix. The average CSS/JS compiler/minifier also has no knowledge about the way how JSF resources are being served. Fortunately, source map URLs can be set by a SourceMap header on the response of the CSS/JS resource. This is thus exactly what the SourceMapResourceHandler does.

It can be installed in the JSF application by below entry in faces-config.xml:

<application>
    <resource-handler>org.omnifaces.resourcehandler.SourceMapResourceHandler</resource-handler>
</application>

It will by default asusme that the source map files are by the build tool placed in the same folder as the minified resources and have a .map extension appended to the resource name. So it will work when you have a some.js resource and a some.js.map file in the same folder. In case it's by the build tool being placed in a subfolder as in sourcemaps/some.js.map, then you can use the below web.xml context parameter to instruct the SourceMapResourceHandler on this.

<context-param>
    <param-name>org.omnifaces.SOURCE_MAP_RESOURCE_HANDLER_PATTERN</param-name>
    <param-value>sourcemaps/*.map</param-value>
</context-param>

How about OmniFaces 2.x and 1.1x?

2.x is basically already since 2.6 in maintenance mode. I.e. only bugfix versions will be released. It's currently already at 2.6.9, also released today. As long as you're still on JSF 2.2 with CDI, you can continue using latest 2.6.x, but it won't contain new things introduced in 3.x.

1.1x is basically already since 2.5 in maintenance mode. I.e. only bugfix versions will be released. It's currently still at 1.14.1 (May 2017), featuring the same features as OmniFaces 2.4, but without any JSF 2.2 and CDI things and therefore compatible with CDI-less JSF 2.0/2.1.

Maven download stats

Here are the 2018's Maven download stats so far:

  • January 2018: 14646
  • February 2018: 14786
  • March 2018: 18059

Below is the pie of March 2018, 3.0 is already on 9%:

Saturday, April 7, 2018

Do not use org.glassfish Mojarra 2.4.0!

Look, there's a Mojarra 2.4.0 in Maven Central:

What is this? Is JSF 2.4 already there?

No, JSF 2.4 is not there yet. Technically speaking, Mojarra 2.4.0 represents the latest state of the master branch as it was during the transfer from Oracle to Eclipse. That transfer took place when JSF 2.3 specification was already released and JSF 2.4 specification has still to be started yet. JSF 2.4 is far from being a beta, let alone a reasonable snapshot. And yet there is a Mojarra 2.4.0 in Maven instead of e.g. a Mojarra 2.4.0-M1. As per the agreement between Oracle and Eclipse, it was necessary to release the latest work on Mojarra under Oracle's umbrella into Maven Central before the transfer to Eclipse was completed. And later Eclipse will do the same after the transfer is completed so that the integrity can be validated by the public. Using version "2.4.0" is indeed way confusing for the public, but it is what it is.

In other words, you should not use it. A few days later, Mojarra 2.3.4 was also released. Use this instead.

<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.faces</artifactId>
    <version>2.3.4</version>
</dependency>

Technically speaking, Mojarra 2.4.0 is even a step back from Mojarra 2.3.4 as to bugfixes. Mojarra 2.4.0 misses among others the fixes on issues 4274, 4313, 4321, 4324, 4330, 4331, 4332, 4337 and 4358.

After the transfer to Eclipse, we'll as first step change the package name (and thus also the Maven coordinates). We already have a lot of other plans on it. High in our wishlist is removing everything which is @Deprecated and JSP related.

Wednesday, January 3, 2018

OmniFaces 3.0 released!

OmniFaces 3.0 has been released!

The minimum requirements have been upgraded from Java 1.7, JSF 2.2, EL 2.2, Servlet 3.0, CDI 1.1 and (optional) BV 1.1 to Java 1.8, JSF 2.3, EL 3.0, Servlet 3.1, CDI 2.0 and (optional) BV 2.0. WebSocket 1.1 hasn't been upgraded since, so it's still the same. As of now, OmniFaces 3.0 is technically still backwards compatible with EL 2.2, CDI 1.1 and BV 1.1, as features in newer versions are still unutilized, but that may change in a future 3.x version. OmniFaces 3.0 will explicitly prevent deployment when Java 1.8, JSF 2.3 or CDI 1.1 are absent in the environment.

To learn what's new in JSF 2.3, head to the complete overview of new JSF 2.3 features in Arjan Tijms' blog.

As shown in what's new in OmniFaces 3.0 at the showcase site, below is a summary of breaking changes, new things and deprecated things in OmniFaces 3.0 as compared to OmniFaces 2.6.8, which is also released today.

Breaking changes:

  • <o:form useRequestURI="true"> is now the default behavior of <o:form> as it was basically the main reason to use <o:form>. I.e. it will always submit to exactly the same URL as in browser's address bar whereas the <h:form> only submits to the forwarded URL without query string, causing among others view parameters and path parameters to get lost every time. You can if necessary disable this by <o:form useRequestURI="false"> or switching back to <h:form>.
  • <o:form> will now by default perform a partial submit on any JSF ajax request. In other words, only the parameters actually covered by execute attribute of <f:ajax> will be sent, hereby reducing the request payload to not contain unnecessary parameters. This is similar to PrimeFaces partialSubmit feature. Even though this has proven to work flawlessly in PrimeFaces for ages, you can if necessary disable this by <o:form partialSubmit="false">.
  • <o:validateBean showMessageFor="@violating"> will not anymore show the "remaining" messages (coming from other bean properties which are not covered by the JSF form) as a global message but just suppress them.
  • ValidateMultipleFields (all multi-field validators basically) have previously skipped all disabled/readonly/nonrendered inputs. Now they won't anymore be skipped, and the values argument (3rd argument of validate method) will contain their current model value (and thus not the submitted value).
  • Package org.omnifaces.component.output.cache has been migrated to org.omnifaces.util.cache and several previously private/hidden artifacts have been made public, so that it's now more useful for non-component related caches.
  • All Faces/FacesLocal/Components/Servlets methods which previously threw IOException will now throw Java8's java.io.UncheckedIOException instead, hereby reducing down unnecessary throws IOException boilerplate in methods because those should always be bubbled up into the container.
  • org.omnifaces.facesviews.FacesServletDispatchMethod and ViewHandlerMode which was deprecated since 2.6 have now been removed without replacement (as they have become superfluous since Servlet 3.0).
  • org.omnifaces.renderkit.Html5RenderKit which was deprecated since 2.2 has now been removed without replacement (as this has become superfluous since JSF 2.2 with new passthrough attribtue feature).
  • org.omnifaces.config.BeanManager which was deprecated since 2.5 has now been removed with org.omnifaces.util.Beans as replacement.
  • RichFaces targeted hacks have been removed. OmniFaces 3.0 is therefore not anymore compatible with RichFaces. Note that RichFaces itself was declared "End of Life" June 2016 and is already not compatible with JSF 2.3.

New things:

Deprecated things:

  • <o:commandScript> has been deprecated as it's now moved into JSF 2.3 as <h:commandScript> with exactly the same functionality.
  • fixviewstate.js has been deprecated as it's now finally solved in JSF 2.3 (by yours truly).
  • <o:form includeViewParams="true"> as well as <o:form includeRequestParams="true"> have been deprecated as those have never proven to be more useful than useRequestURI="true".

Noted should be that the <o:socket> is also moved into JSF 2.3 as <f:websocket> with here and there a few small API generifications. But this won't be deprecated anywhere in OmniFaces 3.x as it still has room for new ideas and improvements.

As to new things, the benefits of <o:selectItemGroups>, omnifaces.ImplicitNumberConverter and Faces#getXxxAttribute() overloads are detailed below.

Not anymore explicitly creating new SelectItemGroup()

When grouping select item options in a <optgroup>, you had to manually bake SelectItemGroup instances yourself. Assuming that you have List<Category> as wherein Category in turn has a List<Product>:

private Product selectedProduct;
private List<SelectItem> categories;

@Inject
private ProductService productService;

@PostConstruct
public void init() {
    categories = productService.listCategories().stream().map(category -> {
        SelectItemGroup group = new SelectItemGroup(category.getName());
        group.setSelectItems(category.getProducts().stream()
            .map(product -> new SelectItem(product, product.getName()))
            .toArray(SelectItem[]::new));
        return group;
    }).collect(Collectors.toList());
}
<h:selectOneMenu value="#{bean.selectedProduct}" converter="omnifaces.SelectItemsConverter">
    <f:selectItem itemValue="#{null}" />
    <f:selectItems value="#{bean.categories}" />
</h:selectOneMenu>

This is too much JSF 1.0. Even in JSF 2.3, there's still nothing like <f:selectItemGroups>. OmniFaces 3.0 therefore brings a <o:selectItemGroups> into the game.

private Product selectedProduct;
private List<Category> categories;

@Inject
private ProductService productService;

@PostConstruct
public void init() {
    categories = productService.listCategories();
}
<h:selectOneMenu value="#{bean.selectedProduct}" converter="omnifaces.SelectItemsConverter">
    <f:selectItem itemValue="#{null}" />
    <o:selectItemGroups value="#{bean.categories}" var="category" itemLabel="#{category.name}">
        <f:selectItems value="#{category.products}" var="product" itemLabel="#{product.name}" />
    </o:selectItemGroups>
</h:selectOneMenu>

Not anymore explicitly entering $ or %

Sometimes you need to have the enduser to input currencies. For that, you'd intuitively use <f:convertNumber type="currency">.

<h:outputLabel for="price" value="Price $" />
<h:inputText id="price" value="#{bean.price}">
    <f:convertNumber type="currency" currencySymbol="$" />
</h:inputText>
<h:message for="price" />

However, this would surprisingly fail with a conversion error when the enduser doesn't explicitly specify the currency symbol in the input field.

Price $
'12.34' could not be understood as a currency value. Example: $99.99

Of course you could work around it by replacing type="currency" with pattern="#,##0.00". But this is embarrassingly harder to remember than just type="currency" and even prone to locale-specific differences. In some locales the fractions are optional or even omitted. And what if the enduser thought to enter the currency symbol anyway?

With the new omnifaces.ImplicitNumberConverter you don't need to worry about this all.

<h:outputLabel for="price" value="Price $" />
<h:inputText id="price" value="#{bean.price}">
    <o:converter converterId="omnifaces.ImplicitNumberConverter" type="currency" currencySymbol="$" />
</h:inputText>
<h:message for="price" />

Entering the currency symbol has now become optional and it will be inferred when absent. On outputting, it will be hidden from the output and it will be assumed that the user interface already covers this. The same facility is also available for type="percent".

Not anymore explicitly checking if scoped attribute exists

You might have repeatedly stumbled into the below use case in some non-managed JSF artifact (where you'd otherwise of course just have used CDI):

public SomeObject getSomeObject() {
    SomeObject someObject = Faces.getRequestAttribute(SomeObject.class.getName());

    if (someObject == null) {
        someObject = new SomeObject();
        Faces.setRequestAttribute(SomeObject.class.getName(), someObject);
    }

    return someObject;
}

The getRequest/Flash/View/Session/ApplicationAttribute() methods have now all an overload which takes a Supplier<T>. The usage is now much like Map#computeIfAbsent().

public SomeObject getSomeObject() {
    return Faces.getRequestAttribute(SomeObject.class.getName(), SomeObject::new);
}

Not anymore explicitly rethrowing IOException

Some utility methods in Faces throw IOException when something fails during handling the HTTP response. Generally this is unavoidable, such as end user facing a local network error. Those exceptions are documented to be rethrown. For example,

@PostConstruct
public void init() throws IOException {
    if (someCondition()) {
        Faces.redirect("other.xhtml");
    }
}

However, redeclaring this everytime is tiresome and moreover, in this specific example it goes against the @PostConstruct contract as it may actually not throw a checked exception. Weld will even warn about this during webapp startup.

With OmniFaces 3.0, all methods which previously threw IOException will now throw it as Java 8's new UncheckedIOException instead. So you can safely strip out any unnecessary throws IOException coming from Faces/FacesLocal/Servlets utility methods over all place.

@PostConstruct
public void init() {
    if (someCondition()) {
        Faces.redirect("other.xhtml");
    }
}

Installation

Non-Maven users: download OmniFaces 3.0 JAR and drop it in /WEB-INF/lib the usual way, replacing the older version if any.

Maven users: use <version>3.0</version>.

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces</artifactId>
    <version>3.0</version>
</dependency>

But my server doesn't support JSF 2.3!

Don't panic. I wrote a clean set of installation instructions in Mojarra README. In a nutshell, for Tomcat 8.x you just need to upgrade the JARs and add JSONP API as compared to Mojarra 2.2 (with CDI + BV). For Java EE 7 (e.g. WildFly 8-11.x, Payara 4.x, TomEE 7.x, etc) you need to manually swap out server-provided JARs as instructed in the README.

How about OmniFaces 2.x and 1.1x?

2.x is basically already since 2.6 in maintenance mode. I.e. only bugfix versions will be released. It's currently already at 2.6.8, also released today. As long as you're still on JSF 2.2 with CDI, you can continue using latest 2.6.x, but it won't contain new things introduced in 3.x.

1.1x is basically already since 2.5 in maintenance mode. I.e. only bugfix versions will be released. It's currently still at 1.14.1 (May 2017), featuring the same features as OmniFaces 2.4, but without any JSF 2.2 and CDI things and therefore compatible with CDI-less JSF 2.0/2.1.

Maven download stats

Here are the 2017's Maven download stats

  • January 2017: 10889
  • February 2017: 12060
  • March 2017: 14669
  • April 2017: 11999
  • May 2017: 12521
  • June 2017: 11535
  • July 2017: 12197
  • August 2017: 13925
  • September 2017: 13502
  • October 2017: 13080
  • November 2017: 14372
  • December 2017: 11735