Creating a simple JSF component: Google URL shortener

Standard

This article shows how to make a simple component in JSF. The component will utilize the google url shortener. The final result will have a custom taglib and can be passed any URL for shortening as follows:

<six:urlshortener id="googleURL" url="http://yoursite.com" />

The custom component will comprise of 4 files: API, Component, Renderer, and Taglib.xml.

The first file is the renderer which is responsible for the visual representation of the component. It extends the javax.faces.render.Renderer class. A responseWriter is used to open and close elements to be written to the DOM. A typical renderer may have both a encodeBegin / encodeEnd method, however for this example it is unnecessary as we only need render tags after encoding the URL from Google. The renderer can be identified by RENDERER_TYPE which corresponds to the Taglib.xml later in this article.

@FacesRenderer(componentFamily = UrlShortenerComponent.COMPONENT_FAMILY, rendererType = UrlShortenerRenderer.RENDERER_TYPE)
public class UrlShortenerRenderer extends Renderer {

    /**
     * * Renderer type of {@link UrlShortenerRenderer}.
     */
    public static final String RENDERER_TYPE = "com.sixthpoint.jsf.google.url.shortener.components.UrlShortenerRenderer";

    private static final Logger LOG = Logger.getLogger(UrlShortenerRenderer.class.getName());

    @Override
    public void encodeEnd(FacesContext context, UIComponent uicomponent) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        UrlShortenerComponent component = (UrlShortenerComponent) uicomponent;
        try {
            writer.startElement("a", component);
            writer.writeAttribute("id", component.getClientId(), "id");

            try {

                String url = component.getGoogleURL(component);
                writer.writeAttribute("href", url, "href");

                String style = (String) component.getAttributes().get("style");
                if (style != null) {
                    writer.writeAttribute("style", style, null);
                }

                writer.write(url);

            } catch (IOException ex) {
                writer.write(ex.getMessage());
                LOG.log(Level.SEVERE, "Could not generate Google short URL", ex);
            }

            writer.endElement("a");
        } catch (IOException ex) {
            LOG.log(Level.SEVERE, "Error generating markup", ex);
        }
    }
}

The renderer calls the component getGoogleUrl method. This component is inserted into the component tree by the componentFamily attribute which has been annotated by @FacesRenderer. The two important methods defined by the UrLShortenerComponent below are: getFamily, and getGoogleURL.

Since the rendering has been delegated, the getFamily method is overrides the extended UI Component. This override returns the identifier, which is used to refer to the component that can be rendered by the renderer. A StateHelper is used to store the value of the url attribute between requests.

The getGoogleURL method is called by the renderer and is intended to represent the meaning / purpose of the component. The class is extended from the javax.faces.component.UIComponent and is bound by its component name to the renderer during the request-processing lifecycle.

@FacesComponent(UrlShortenerComponent.COMPONENT_TYPE)
public class UrlShortenerComponent extends UIComponentBase {

    /**
     * * Component family of {@link UrlShortenerComponent}.
     */
    public static final String COMPONENT_FAMILY = "UrlShortener";

    /**
     * * Component type of {@link UrlShortenerComponent}.
     */
    public static final String COMPONENT_TYPE = "UrlShortener";

    /**
     * Attribute name constant for url.
     */
    private static final String URL_TAG = "url";

    /**
     * Default value for the url attribute.
     */
    private static final String URL_DEFAULT = "http://www.sixthpoint.com/";

    /**
     * Set the component tree family name
     *
     * @return
     */
    @Override
    public String getFamily() {
        return UrlShortenerComponent.COMPONENT_FAMILY;
    }

    /**
     * Perform API call
     *
     * @param component
     * @return
     * @throws IOException
     */
    public String getGoogleURL(UIComponent component) throws IOException {

        UrlShortenerAPI api = new UrlShortenerAPI();
        return api.getGoogleURL(getUrl());
    }

    /**
     * Get the tag value from the state helper
     *
     * @return
     */
    public String getUrl() {
        return (String) getStateHelper().eval(URL_TAG, URL_DEFAULT);
    }

    /**
     * Set the tag value to the state helper
     *
     * @param url
     */
    public void setUrl(String url) {
        getStateHelper().put(URL_TAG, url);
    }

}

The final piece of the puzzle is the API. This file is where the call to Google’s API to encode the URL is made. The URLShortenerAPI is a POJO class intended to separate UiComponent logic from business logic.

public class UrlShortenerAPI {

    /**
     * URL to the REST service.
     */
    private static final String API_URL = "https://www.googleapis.com/urlshortener/v1/url";

    /**
     * Property in the JSON output to be extracted.
     */
    private static final String PROPERTY_CONTAINING_OUTPUT = "id";

    /**
     * Gets the shortened URL from Google.
     *
     * @param url
     * @return String
     * @throws IOException
     */
    public String getGoogleURL(String url) throws IOException {

        OAuthRequest oAuthRequest = new OAuthRequest(Verb.POST, API_URL);

        // Set header content
        oAuthRequest.addHeader("Content-Type", "application/json");

        // Payload
        oAuthRequest.addPayload("{\"longUrl\": \"" + url + "\"}");

        // Send the request
        Response r = oAuthRequest.send();

        // Determine generic map type
        Type typeOfMap = new TypeToken<Map<String, String>>() {
        }.getType();

        // Json -> Map
        Map<String, String> responseMap = new GsonBuilder().create().fromJson(r.getBody(), typeOfMap);

        return responseMap.get(PROPERTY_CONTAINING_OUTPUT);

    }
}

To use this component properly you need to configure a taglib. This taglib file is to be placed in the /WEB-INF/googleurlshortener.taglib.xml. I have created my own namespace as seen below. Notice that it defines the same component-type, renderer-type, and attributes needed to statisfy the component.

<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib
    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-facelettaglibrary_2_0.xsd"
    version="2.0">
    <namespace>http://com.sixthpoint.google/urlshortener</namespace>
    <tlib-version>1.0</tlib-version>
    <jsp-version>2.2</jsp-version>
    <short-name>googleurlshortener</short-name>
    <uri>http://www.sixthpoint.com/</uri>
    <tag>
        <tag-name>urlshortener</tag-name>
        <component>
            <component-type>UrlShortener</component-type>
            <renderer-type>com.sixthpoint.jsf.google.url.shortener.components.UrlShortenerRenderer</renderer-type>
        </component>
        <attribute>
            <name>url</name>
            <type>java.lang.String</type>
            <description>The url that is to be shortened.</description>
        </attribute>
    </tag>
</facelet-taglib>

To use add the namespace to your file with whatever prefix you define and call the urlshortener method.

The complete project project code can be found on GitHub.