Sunday, September 06, 2015

Book Review: Murach's Java Servlets and JSP

Cyndi Vasquez sent me this title a while back. I have been so bogged down that I finally got a chance to look at it. Thanks Murach... this is a good book.

The book is written in a style that many will either love, or hate. I personally am on the former. The pages on the left side are an explanation of the technical matter, and the right side are examples. The book is designed to guide you from front to back on an adventure into programming. Along the safari, I assure you will be pleased. The quarry is knowledge and this book is the tool to capture it.

The books is divided into five sections which will take you from absolute beginner to accomplished Servlet and JSP aficionado. It will even teach you a little about databases, and JSF along the way. There are two appendices that will show you how to set up your computer either Mac, or PC before you get started on your journey.

One of the thrills about this book is the use of NetBeans. I believe that NetBeans is the easiest tool for developing web based applications using Servlets and JSP. It also is my preferred tool for Java development in general. The book not only teaches you about the essentials of the title technologies, but helps you gain an in-depth knowledge of a valuable IDE that you can bank your career on.

Section one covers the basics of the technology and the MVC pattern. This is followed by a crash course on HTML, CSS, and web technologies based on Java.  The 8th chapter on EL is a great reference for how to use this important technology in your web applications.

Section three covers essential database skills to get you started with web development. These skills are the bare minimum to get started, but are complete for the purpose of this book. Keep in mind, these are the basics.

Section four is the meat of the book. This covers the advanced Servlet and JSP skills. It also covers some additional technologies like JSF. My favorite parts of this section are chapters 18, 19, and 20. The HTTP Request and Response skills are something every developer should strive to make sure they understand. Chapter 19 covers listeners which are truly your friends. Chapter 20 covers another often misused, abused, and otherwise fantastic technology. The filter can make your life as a web developer a great pleasure, or a rabbit hole in which you feel like Alice in Wonderland.

The final concluding section five puts all of the pieces together in a Music Store website. The Music Store website uses most of the technologies covered, and takes the learner to the next level with clear and concise directions.

Overall this is a great book for anyone interested in learning about Servlet and JSP technology. Please keep in mind that all Java web technologies are based on the Servlet foundation. JSF is nothing more than a veneer on top of this technology as an example.

Wednesday, April 01, 2015

JSF 2.2 Tip of the Day: p:passthrough and How to use it

I was asking my team to go through their JSF pages, and to update the XML namespaces to use the latest namespace from the JSF 2.2 specification. While I was looking at the code, I found a number of instances where developers were adding attributes like name to <h:commandButton /> and NetBeans correctly was identifying that there is an issue with that.

Fortunately, some of these attributes were passing through to the underlying page without needing p:passthrough. However, you should not rely on such functionality to work. If the VDL Document does not show it as an attribute, you shouldn't expect it to work.

Alright, so how do we do it correctly?

There is no magic here. It is simply a matter of adding the attribute with a prefix of p:, for example p:name="someName" for the name attribute. This will result in the attribute being passed through the rendered and added to the resulting output.

So I have an example, and the resulting output.

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://xmlns.jcp.org/jsf/passthrough">
<h:head>
<title>&lt;p:passthrough/&gt; example</title>
</h:head>
<h:body>
<h:panelGroup id="panelGroup1" layout="block"
p:onmouseover="this.setAttribute('style', 'color: blue; font-size:32px; font-weight:bolder;');"
p:onmouseout="this.setAttribute('style', 'color: black; font-size:inherit; font-weight:normal;');">
<h:outputText id="outputText1" value="Click me!"
p:data-experimental-type="Hello World!"
p:style="font-variant:small-caps;"
p:onclick="alert(this.getAttribute('data-experimental-type'));"/>
</h:panelGroup>
</h:body>
</html>
view raw index.xhtml hosted with ❤ by GitHub
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="j_idt2">
<title>&lt;p:passthrough/&gt; example</title>
</head>
<body>
<h1>&lt;p:passthrough/&gt; example</h1>
<div id="panelGroup1"
onmouseout="this.setAttribute('style', 'color: black; font-size:inherit; font-weight:normal;');"
onmouseover="this.setAttribute('style', 'color: blue; font-size:32px; font-weight:bolder;');">
<span id="outputText1" onclick="alert(this.getAttribute('data-experimental-type'));"
data-experimental-type="Hello World!"
style="font-variant:small-caps;">Click me!</span>
</div>
</body>
</html>
view raw output.html hosted with ❤ by GitHub
The resulting output will run the JavaScript associated with the passed through attributes, or set the CSS styling. Very simple and easy to implement.

Monday, March 30, 2015

Customizing Blogger

Introduction

I want to start by blaming Markus Eisele for my misadventures. I was looking at his blog and liked the makeover he gave it. I did find the template he was using, but decided that I would write my own. Well I thought this should be an easy thing to do. I severely underestimated the challenge of making a custom template for myself. It turns out that one of the easiest things turns out to be the hardest. Alright Markus you are off the hook... it may just be my bravado, and belief I can build a better mousetrap that led me down the long lonesome road.

Google is well known for having good and sometimes great APIs for their technology. Blogger is an exception to that rule. There is not one clear cut schema for their layout that I can find ironically using Google itself. You figure with all of the templates and bloggers that this would be covered ad nauseum.  It is not though.

Technologies

I was looking for a simple and elegant framework to make my blog sites look professional, and also make them portable. I had the following requirements:
  1. Mature framework
  2. Can be found on a CDN
  3. Easy to use
  4. Simple to implement
  5. Well Documented
  6. Lots of examples
  7. Flexible
  8. Customizable
  9. Response UI
  10. HTML5
  11. Portable
  12. JSF Compatible
  13. Works with NetBeans IDE for Tooling
I looked at a number of frameworks including Foundation, and Bootstrap. I ended up choosing Foundation since it seemed to be easier to use for me. Your milage my vary.

The first thing I wanted to know was what was the minimum required for a template on Blogger. I discovered that are a couple of versions of the template: an HTML 4.01 version (v.1) and an HTML 5 version (v.2) which are somewhat a hybrid mix of XML, and (X)HTML. I published the basic templates on Gist as shown below.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns='http://www.w3.org/1999/xhtml'
xmlns:b='http://www.google.com/2005/gml/b'
xmlns:data='http://www.google.com/2005/gml/data'
xmlns:expr='http://www.google.com/2005/gml/expr'>
<head>
<title><data:blog.pageTitle/></title>
<b:skin>
<![CDATA[
<!-- SKIN CONTENTS -->
]]>
</b:skin>
</head>
<body>
<!-- BODY CONTENTS -->
</body>
</html>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<html b:version='2' class='v2' expr:dir='data:blog.languageDirection'
xmlns='http://www.w3.org/1999/xhtml'
xmlns:b='http://www.google.com/2005/gml/b'
xmlns:data='http://www.google.com/2005/gml/data'
xmlns:expr='http://www.google.com/2005/gml/expr'>
<head>
<title><data:blog.pageTitle/></title>
<b:skin>
<![CDATA[
<!-- SKIN CONTENTS -->
]]>
</b:skin>
</head>
<body>
<!-- BODY CONTENTS -->
</body>
</html>
I have a couple of different blogs and found them to be different so I thought I would share my findings.

The next thing I needed to find out was what was the minimal template I would need for using with Foundation. The template below uses a CDN to deliver the required JS/CSS. The template below is the culmination of a lot of work to make it work with the visual tools on Blogger. Remember to backup your existing template before installing mine.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml'
xmlns:b='http://www.google.com/2005/gml/b'
xmlns:data='http://www.google.com/2005/gml/data'
xmlns:expr='http://www.google.com/2005/gml/expr'
b:version='2' class='v2 no-js' expr:dir='data:blog.languageDirection'>
<head>
<meta content='width=device-width, initial-scale=1.0' name='viewport'/>
<b:include data='blog' name='all-head-content'/>
<title>
<data:blog.pageTitle/>
</title>
<link href='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/css/foundation.min.css' rel='stylesheet'/>
<link href='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/css/normalize.min.css' rel='stylesheet'/>
<link href='https://cdnjs.cloudflare.com/ajax/libs/foundicons/3.0.0/foundation-icons.min.css' rel='stylesheet'/>
<b:skin>
<![CDATA[
<!-- Add Custom CSS Here -->
]]>
</b:skin>
<b:include data='blog' name='google-analytics'/>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/vendor/modernizr.js' type='text/javascript'>
</script>
</head>
<body>
<!-- NAVBAR -->
<b:section class='navbar' id='navbar' maxwidgets='1' showaddelement='no'>
<b:widget id='Navbar1' locked='true' title='Navbar' type='Navbar'></b:widget>
</b:section>
<!-- META DATA -->
<b:if cond='data:blog.pageType == &quot;index&quot;'>
<div itemscope='itemscope' itemtype='http://schema.org/Blog' style='display: none;'>
<meta expr:content='data:blog.title' itemprop='name'/>
<b:if cond='data:blog.metaDescription'>
<meta expr:content='data:blog.metaDescription' itemprop='description'/>
</b:if>
</div>
</b:if>
<!-- HEADER -->
<div class='row'>
<div class='large-12 medium-12 small-12 columns'>
<b:section class='header' id='header' maxwidgets='1' showaddelement='no'>
<b:widget id='Header1' locked='true' title='Test Blog (Header)' type='Header'></b:widget>
</b:section>
</div>
</div>
<!-- MAIN -->
<div class='row'>
<!-- COLUMN 1 - MAIN -->
<div class='large-9 medium-9 small-9 columns'>
<b:section class='main' id='main' showaddelement='yes'>
<b:widget id='Blog1' locked='true' title='Blog Posts' type='Blog'></b:widget>
<b:widget id='PopularPosts1' locked='false' title='Popular Posts' type='PopularPosts'></b:widget>
</b:section>
</div>
<!-- COLUMN 2 - SIDEBAR -->
<div class='large-3 medium-3 small-3 columns'>
<b:section class='sidebar' id='sidebar' showaddelement='yes'>
<b:widget id='BlogArchive1' locked='false' title='Archive' type='BlogArchive'></b:widget>
<b:widget id='CustomSearch1' locked='false' title='Search' type='CustomSearch'></b:widget>
<b:widget id='Label1' locked='false' title='Labels' type='Label'></b:widget>
</b:section>
</div>
</div>
<!-- FOOTER -->
<div class='row'>
<div class='large-12 medium-12 small-12 columns'>
<b:section class='foot' id='footer-3' showaddelement='yes'>
<b:widget id='Attribution1' locked='true' title='' type='Attribution'></b:widget>
</b:section>
</div>
</div>
<!-- CDN JS Required for Foundation -->
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/vendor/jquery.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/vendor/jquery.cookie.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/vendor/placeholder.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/vendor/fastclick.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.abide.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.accordion.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.alert.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.clearing.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.dropdown.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.equalizer.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.interchange.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.joyride.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.magellan.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.offcanvas.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.orbit.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.reveal.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.slider.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.tab.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.tooltip.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.topbar.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.topbar.min.js' type='text/javascript'>
</script>
<script>
$(document).foundation();
</script>
</body>
</html>

Conclusion

I finally have a working blog site using the new template, and will update all of my sites to use it. My personal non-technical blog site was the first to use the new template. It is still a work in progress, but it looks very nice. Take a peek for yourself at John Yeary Blogger site.

I have compiled a list of links that I found helpful in trying to figure out their layouts and tags in the references below.

References

Tags

Template References

Additional References

Friday, March 27, 2015

A Simple Method to invoke @PreDestroy on a Class

I was experimenting with how to invoke a @PreDestroy annotated method in a class. This will approach will work with other annotations as well.
/*
* Copyright 2016 Blue Lotus Software, LLC.
* Copyright 2015 John Yeary <jyeary@bluelotussoftware.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.bluelotussoftware.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.Map;
import javax.annotation.PreDestroy;
/**
*
* @author John Yeary <jyeary@bluelotussoftware.com>
* @version 2.0
*/
public class Utils {
/**
* This method reflectively examines the provided object looking for
* {@link PreDestroy} annotations, invokes the first method with the
* annotation found, and returns.
*
* @param object The object to examine.
* @return {@code true} if successful, and {@code false} otherwise.
* @throws SecurityException If a security manager, <i>s</i>, is present and
* any of the following conditions is met:
* <ul>
* <li> the caller's class loader is not the same as the class loader of
* this class and invocation of
* {@link SecurityManager#checkPermissions#checkPermission(java.security.Permission)}
* method with {@code RuntimePermission("accessDeclaredMembers")} denies
* access to the declared methods within this class.</li>
*
* <li> the caller's class loader is not the same as or an ancestor of the
* class loader for the current class and invocation of
* {@link SecurityManager#checkPackageAccess#checkPackageAccess(java.lang.String)}
* denies access to the package of this class.</li>
* </ul>
* @throws IllegalArgumentException if the method is an instance method and
* the specified object argument is not an instance of the class or
* interface declaring the underlying method (or of a subclass or
* implementor thereof); if the number of actual and formal parameters
* differ; if an unwrapping conversion for primitive arguments fails; or if,
* after possible unwrapping, a parameter value cannot be converted to the
* corresponding formal parameter type by a method invocation conversion.
* @throws IllegalAccessException if this {@code Method} object is enforcing
* Java language access control and the underlying method is inaccessible.
* @throws InvocationTargetException if the underlying method throws an
* exception.
*/
public static boolean destroy(final Object object) throws SecurityException,
IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Class clazz = object.getClass();
String methodName = null;
synchronized (object) {
boolean success = false;
try {
Method[] methods = clazz.getDeclaredMethods();
done:// Break for multiple for loops.
for (Method method : methods) {
Annotation[] annotations = method.getDeclaredAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof PreDestroy) {
methodName = method.getName();
if (!method.isAccessible()) {
method.setAccessible(true);
}
method.invoke(object, (Object[]) null);
success = true;
break done;
}
}
}
} catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
System.err.println(MessageFormat.format("An exception occurred while trying to process @PreDestroy on : {0}.{1}(). The message follows: {2}", clazz.getName(), methodName, ex.getMessage()));
throw ex;
}
return success;
}
}
/**
*
* This method reflectively examines the provided map looking for objects
* with {@link PreDestroy} annotations, invokes the first method with the
* annotation found, and returns.
*
* @param map A map of objects to examine for {@link PreDestroy}
* annotations.
* @throws SecurityException If a security manager, <i>s</i>, is present and
* any of the following conditions is met:
* <ul>
* <li> the caller's class loader is not the same as the class loader of
* this class and invocation of
* {@link SecurityManager#checkPermissions#checkPermission(java.security.Permission)}
* method with {@code RuntimePermission("accessDeclaredMembers")} denies
* access to the declared methods within this class.</li>
*
* <li> the caller's class loader is not the same as or an ancestor of the
* class loader for the current class and invocation of
* {@link SecurityManager#checkPackageAccess#checkPackageAccess(java.lang.String)}
* denies access to the package of this class.</li>
* </ul>
* @throws IllegalArgumentException if the method is an instance method and
* the specified object argument is not an instance of the class or
* interface declaring the underlying method (or of a subclass or
* implementor thereof); if the number of actual and formal parameters
* differ; if an unwrapping conversion for primitive arguments fails; or if,
* after possible unwrapping, a parameter value cannot be converted to the
* corresponding formal parameter type by a method invocation conversion.
* @throws IllegalAccessException if this {@code Method} object is enforcing
* Java language access control and the underlying method is inaccessible.
* @throws InvocationTargetException if the underlying method throws an
* exception.
* @see Utils#destroy(java.lang.Object)
* @since 2.0
*/
public static void destroy(final Map<String, Object> map) throws SecurityException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
if (map != null) {
Map<String, Object> synchronizedMap = Collections.synchronizedMap(map);
if (!synchronizedMap.isEmpty()) {
for (Object o : synchronizedMap.values()) {
destroy(o);
}
}
}
}
}
view raw Utils.java hosted with ❤ by GitHub

Tuesday, February 17, 2015

JSF 2.x Tip of the Day: Implementing a ViewMapListener

A map of the lands where the Trobadors flourished. 
"France 1154-en" by Reigen - Own work
Licensed under CC BY-SA 4.0 via Wikimedia Commons.

Introduction

There are a number of SystemEvents supported by JSF 2.x. A question that comes up frequently is how to implement them. In a number of cases on stackoverflow, it is implemented using a PhaseListener. I was looking for a way to cleanup the view map, or just get values from it before it was destroyed. I decided that the simplest way to do so was to implement a ViewMapListener. I also noticed that there were very few posts on how to implement it using the faces-config.xml so I decided to use that approach since it was instructive and more clear to me.

Implementation

The basic implementation requires that you add our listener implementation to the faces-config.xml. The example I have here is designed to get called on a PreDestroyViewMapEvent which is called on a normal navigation. We can force it though by adding a @PreDestroy annotation to a method to invoke before being destroyed. Inside the method we would need to get the UIViewroot view map, and call clear(). This would cause our listener to be invoked too. It would be a good cleanup mechanism for cleaning up resources on session expiration too, but at the moment this does not work on JSF 2.1. The @PreDestroy is not called on session timeout on JSF 2.1. This is expected to be an enhancement in JSF 2.2+.

The code for the project can be downloaded from Bitbuket here: viewmaplistener-example

package com.bluelotussoftware;
import com.sun.faces.application.view.ViewScopeManager;
import java.util.Collections;
import java.util.Map;
import javax.faces.component.TransientStateHelper;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.PreDestroyViewMapEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.ViewMapListener;
/**
* This class is an implementation of {@link ViewMapListener} which listens for
* {@link SystemEvent} on the {@link UIViewRoot}. If the event is a
* {@link PreDestroyViewMapEvent}, the listener will clear the session map of
* the {@link ViewScopeManager#VIEW_MAP_ID} associated with the current view.
*
* @author John Yeary <jyeary@bluelotussoftware.com>
* @version 1.0
* @see ExternalContext#getSessionMap()
* @see ViewScopeManager#ACTIVE_VIEW_MAPS
* @see ViewScopeManager#VIEW_MAP_ID
* @see TransientStateHelper#getTransient(java.lang.Object)
*/
public class ViewMapListenerImpl implements ViewMapListener {
/**
* {@inheritDoc}
*/
@Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
if (event instanceof PreDestroyViewMapEvent) {
PreDestroyViewMapEvent pdvme = (PreDestroyViewMapEvent) event;
UIViewRoot root = (UIViewRoot) pdvme.getComponent();
FacesContext fctx = FacesContext.getCurrentInstance();
String key = (String) root.getTransientStateHelper().getTransient(ViewScopeManager.VIEW_MAP_ID);
System.out.println("Found " + ViewScopeManager.VIEW_MAP_ID + ": " + key);
Map<String, Object> viewMaps = (Map<String, Object>) fctx.getExternalContext().getSessionMap().get(ViewScopeManager.ACTIVE_VIEW_MAPS);
Map<String, Object> map = Collections.synchronizedMap(viewMaps);
synchronized (map) {
System.out.println("Views in session map: " + map.keySet().toString());
map.remove(key);
System.out.println("View removed from " + ViewScopeManager.ACTIVE_VIEW_MAPS);
System.out.println("The remaining views are: " + map.keySet().toString());
}
}
}
/**
* {@inheritDoc}
*
*/
@Override
public boolean isListenerForSource(Object source) {
return source instanceof UIViewRoot;
}
}
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<application>
<system-event-listener>
<system-event-listener-class>com.bluelotussoftware.ViewMapListenerImpl</system-event-listener-class>
<system-event-class>javax.faces.event.PreDestroyViewMapEvent</system-event-class>
<source-class>javax.faces.component.UIViewRoot</source-class>
</system-event-listener>
</application>
</faces-config>

Conclusion

The example above is just one mechanism of using a SystemEvent listener. You may decide to read values from the map, and add them to the session, or manipulate it in some other way before the data is destroyed.

Popular Posts