Pages

Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

Friday, March 1, 2013

How to use Google's guava library to filter and transform a collection

Guava library provides a very large and powerful set of functions over collections, caching, concurrency, annotations among other things.

In this post I will show you how to filter and transform ('map' in functional terms) a collection using guava. It's easy to understand and natural if you have experience in functional programming.

I'll start with a simple way to add items to a collection:

...
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
...
Iterables.addAll(dest, source);


This method simply adds all elements in source to dest. The signature of the addAll method is:

public static <T> boolean addAll(Collection<T> dest, Iterable<? extends T> source)

In this step I'll filter source:

Iterables.addAll(dest, Iterables.filter(source, Predicates.not(Predicates.containsPattern("\\.")));

I'm asuming that both dest and source are collections of Strings. In the previous line, I'm adding all elements in source that don't contain a ".".

Finally suppose you want to transform the source collection to add a suffix to each element before adding it to dest.

Iterables.addAll(dest, Iterables.transform(
    Iterables.filter(source, Predicates.not(Predicates.containsPattern("\\."))),
    new Function < String, String >() {
 public String apply(String input) {
     return input + ".xml";
 }
    }));

transform takes two arguments: the source Iterable and a Function that perform the transformation over each element in the source. Here I'm using String as both the type of the elements in the input collection and the elements in the returned collection, but transform can take a collection of some type and the function would return the collection of some other type, for example:

Iterables.addAll(dest, Iterables.transform(otherSource, new Function < Integer, String >() {
    public String apply(Integer input) {
 return input.toString();
    }
}));

That's it. Later I hope to post other uses's examples of this library.

Monday, December 17, 2012

Solving character encoding issues in JSF

When developing a non-english webapp, you'll probably encounter some issues with special characters as "ñ", "á", etc, ie, when you input some of this characters in a text input field, you get those characters back from the server rendered as "Ö" or something similar.

The best approach is to use UTF-8 enconding all over the application so the response is not encoded differently depending of the place that handles that response.

This approach applies to:

  • Tomcat 6
  • Java 1.6
  • MyFaces 2.1.10
  • RichFaces 4.2.3-Final


The places to configure the encoding are:

  • server.xml in Tomcat

    It's neccesary to get Tomcat to encode GET parameters. This is done by adding the URIEncoding attribute to the HTTP connector.

          <Connector port="8080" protocol="HTTP/1.1" URIEncoding="UTF-8"
           .... />


  • Servlet Filter

    Implement a servlet filter to do the UTF-8 encoding of  the requests and responses:

    public class SetCharacterEncodingFilter implements Filter {
    
        protected String encoding = null;
        protected FilterConfig filterConfig = null;
        protected boolean ignore = true;
    
        /**
         * Take this filter out of service.
         */
        public void destroy() {
            this.encoding = null;
            this.filterConfig = null;
        }
    
        /**
         * Select and set (if specified) the character encoding to be used to
         * interpret request parameters for this request.
         * @param request The servlet request we are processing
         * @param result The servlet response we are creating
         * @param chain The filter chain we are processing
         * @exception IOException if an input/output error occurs
         * @exception ServletException if a servlet error occurs
         */
        public void doFilter(ServletRequest request, ServletResponse response,
                             FilterChain chain)
     throws IOException, ServletException {
    
            // Conditionally select and set the character encoding to be used
            if (ignore || (request.getCharacterEncoding() == null)) {
                String encoding = selectEncoding(request);
                if (encoding != null)
                    request.setCharacterEncoding(encoding);
            }
    
            response.setContentType("text/html; charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            // Pass control on to the next filter
            chain.doFilter(request, response);
        }
    
        /**
         * Place this filter into service.
         * @param filterConfig The filter configuration object
         */
        public void init(FilterConfig filterConfig) throws ServletException {
     this.filterConfig = filterConfig;
            this.encoding = filterConfig.getInitParameter("encoding");
            String value = filterConfig.getInitParameter("ignore");
            if (value == null)
                this.ignore = true;
            else if (value.equalsIgnoreCase("true"))
                this.ignore = true;
            else if (value.equalsIgnoreCase("yes"))
                this.ignore = true;
            else
                this.ignore = false;
        }
    
        /**
         * Select an appropriate character encoding to be used, based on the
         * characteristics of the current request and/or filter initialization
         * parameters.  If no character encoding should be set, return
         * null.
         * * The default implementation unconditionally returns the value configured
         * by the encoding initialization parameter for this
         * filter.
         *
         * @param request The servlet request we are processing
         */
        protected String selectEncoding(ServletRequest request) {
            return (this.encoding);
        }
    
    }
    
    

    Then you have to reference and map that filter in web.xml:

    <filter>
       <filter-name>SetCharacterEncodingFilter</filter-name>
       <filter-class>com.foo.filters.SetCharacterEncodingFilter</filter-class>
       <init-param>
          <param-name>encoding</param-name>
          <param-value>UTF-8</param-value>
       </init-param>
    </filter>
    <filter-mapping>
       <filter-name>SetCharacterEncodingFilter</filter-name>
       <url-pattern>*.xhtml</url-pattern>
    </filter-mapping>


    Here I mapped only xhtml files because I'm also encoding the responses to UTF-8, so mapping images and other resources as UTF-8 will probably generate an encoding error.

  • .xhtml Files

    One final step is neccesary to ensure we have coherent encoding all across the webapp.

    The first line in the xhtml files must be:
    <?xml version="1.0" encoding="UTF-8"?>

         Then, inside the head tag of the xhtml, add the following meta tag:
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    This is a lot less painful if you are using facelet templating technique; if that is the case, you'd only have to add the line mentioned above in the master template instead of in every .xhtml file.





Hope this helps, comment if you have any problem using this aproach.



Tuesday, December 11, 2012

How to setup a masked input for rich:calendar in RichFaces

In richfaces you can use jquery to set up masked inputs. First, you must download masked input plugin for the jQuery javascript library and reference it in some part of the page:

<h:outputScript name="js/jquery.maskedinput-1.3.min.js" target="head" />

Then, to mask some input field you should do:


<h:inputText id="date" value="#{bBean.date}" >
    <rich:jQuery selector="#date" query="mask('99/99/9999',{placeholder:'_'})" />
</h:inputText>

Unfortunely, the same doesn't work for rich:calendar. Richfaces automatically renders rich:calendar into an input field for the date text and a div for the calendar itself. The input field is generated with the id of the rich:calendar tag plus "inputDate" as a prefix: ie, if the calendar has id="dob", the generated input field will have the id="dobInputDate"; so one workaround could be:

<h:form id="someForm">
    ...

    <rich:calendar id="date" value="#{bBean.date}"
        enableManualInput="true" popup="true" showApplyButton="false" >
    </rich:calendar>
    <rich:jQuery selector="#someForm\:dateInputDate" query="mask('99/99/9999',{placeholder:'_'})" />
    ...
</h:form>

The attribute enableManualInput of the calendar should be set to true, otherwise it doesn't make sense to mask the input field.


Friday, December 7, 2012

How to render xhtml with images using flying saucer in a servlet container

If you need to render an xhtml file to produce a PDF, you can use flying saucer xhtmlrenderer library. You can do that in a servlet container by implementing a Filter:

public class RendererFilter implements Filter {

    private FilterConfig config;

    private DocumentBuilder documentBuilder;

    /**
     * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
     */
    public void init(FilterConfig configToSet) throws ServletException {
        try {
            this.config = configToSet;
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            documentBuilder = factory.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            throw new ServletException(e);
        }
    }

    /**
     * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse,
     *      javax.servlet.FilterChain)
     */
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException,
        ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        // Check to see if this filter should apply.
        String renderType = request.getParameter("RenderOutputType");
        if (renderType != null) {
            // Capture the content for this request
            ContentCaptureServletResponse capContent = new ContentCaptureServletResponse(response);
            filterChain.doFilter(request, capContent);

            try {
                // Parse the XHTML content to a document that is readable by the XHTML renderer.
                StringReader contentReader = new StringReader(capContent.getContent());
                InputSource source = new InputSource(contentReader);
                Document xhtmlContent = documentBuilder.parse(source);

                if (renderType.equals("pdf")) {
                    ITextRenderer renderer = new ITextRenderer();
                    renderer.setDocument(xhtmlContent, "");
                    renderer.layout();

                    response.setContentType("application/pdf");
                    response.setHeader("Content-Disposition", "inline; filename=print.pdf");
                    OutputStream browserStream = response.getOutputStream();
                    renderer.createPDF(browserStream);
                    return;
                }

            } catch (SAXException e) {
                throw new ServletException(e);
            } catch (DocumentException e) {
                throw new ServletException(e);
            }

        } else {
            // Normal processing
            filterChain.doFilter(request, response);
        }
    }
}

The content of the xhtml file is obtained from the response using a wrapper that captures the response writer and produces a ByteArrayOutputStream that is converted to a string in the getContent method:

public class ContentCaptureServletResponse 
                            extends HttpServletResponseWrapper {
   private ByteArrayOutputStream contentBuffer;
   private PrintWriter writer;
   public ContentCaptureServletResponse(HttpServletResponse resp) {
      super(resp); 
   }
   @Override
   public PrintWriter getWriter() throws IOException {
      if(writer == null){
         contentBuffer = new ByteArrayOutputStream();
         writer = new PrintWriter(contentBuffer);
      }
      return writer;
   }
   public String getContent(){
      writer.flush();
      String xhtmlContent = new String(contentBuffer.toByteArray());
      return xhtmlContent; 
   }
}

Source code obtained from here.


This works fine except in the case that the xhtml has images in it. In that case, the renderer library must kwow the url that the xhtml came from, in order to calculate relative image paths. To achieve that you can change the line 45 of the RendererFilter: 

   renderer.setDocument(xhtmlContent, "");

by:  

   renderer.setDocument(xhtmlContent, request.getRequestURL().toString());

Before you can use the renderer, you must configure the filter and mapping in the web.xml of the application.

Finally, to automatically generate the PDF on the fly you must call or link to the url in the following way:

../path/to/file.xhtml?RenderOutputType=pdf


Thursday, December 6, 2012

Avoid closing a rich:popupPanel in richfaces when validation error occurs

In richfaces is common to have a rich:popupPanel that allows some input. If you must validate that input, you may want to left the panel open when a validation error occurs to show error messages. See the following picture that shows the case:




You can achieve this using the oncomplete attribute of the a4j:commandButton tag.


<rich:popupPanel 
  id="cp" minHeight="200" minWidth="450" modal="true"
     domElementAttachment="form" autosized="true">
....


   <a4j:commandButton
      value="Save"
      disabled="#{not securityScope.userInRole['ROLE_ABM']}"
  action="#{backingBean.save}"
      oncomplete="if (#{facesContext.maximumSeverity==null}) 
                  #{rich:component('cp')}.hide(); return false;">
   </a4j:commandButton>
...
</rich:popupPanel>

The important part is  #{facesContext.maximumSeverity == null} to ensure that there are no errors present after validation.

Wednesday, December 5, 2012

Migration from JSF 1.x to JSF 2

This is one of the things you have to change when migrating your JSF-Spring application from JSF 1.x to JSF 2.

In JSF 1.x, to get access to a spring bean in a backing bean or in a converter, you would do:

FacesContext context = FacesContext.getCurrentInstance();
MyService myService = (MyService) context.getApplication().createValueBinding("#myService}").getValue(context);

In JSF 2 this is replaced by:

FacesContext context = FacesContext.getCurrentInstance();
MyService myService = (MyService) context.getELContext().getELResolver().getValue(context.getELContext(), null, "myService");


Another way to do it would be:

FacesContext context = FacesContext.getCurrentInstance();
String remoteUser = context.getApplication().evaluateExpressionGet(context, "#myService.currentUser}", String.class);