Matt RaibleMatt Raible is a writer with a passion for software. Connect with him on LinkedIn.

The Angular Mini-Book The Angular Mini-Book is a guide to getting started with Angular. You'll learn how to develop a bare-bones application, test it, and deploy it. Then you'll move on to adding Bootstrap, Angular Material, continuous integration, and authentication.

Spring Boot is a popular framework for building REST APIs. You'll learn how to integrate Angular with Spring Boot and use security best practices like HTTPS and a content security policy.

For book updates, follow @angular_book on Twitter.

The JHipster Mini-Book The JHipster Mini-Book is a guide to getting started with hip technologies today: Angular, Bootstrap, and Spring Boot. All of these frameworks are wrapped up in an easy-to-use project called JHipster.

This book shows you how to build an app with JHipster, and guides you through the plethora of tools, techniques and options you can use. Furthermore, it explains the UI and API building blocks so you understand the underpinnings of your great application.

For book updates, follow @jhipster-book on Twitter.

10+ YEARS


Over 10 years ago, I wrote my first blog post. Since then, I've authored books, had kids, traveled the world, found Trish and blogged about it all.
You searched this site for "param". 72 entries found.

You can also try this same search on Google.

Zero Configuration in Struts 2

Struts 2 has a nifty zero configuration feature. However, it's only useful for registering actions, not for automatically registering results. In other words, you still have to use an @Result annotation to tell your action what page to dispatch to. To use default view names instead of requiring @Result, you can use the Codebehind Plugin. Also, did you know Struts 2 will autowire your Spring dependencies? It's pretty slick.

What does this all mean? It means you can write your Struts 2 application without writing any XML. Of course, you can still use XML to tweak behavior, but with these plugins enabled, you won't have to.

IMO, these plugins should be combined into a single zero configuration feature.

Here's how you can enable Struts 2's Zero Configuration feature in AppFuse 2.0:

  1. Add a packageNames parameter to the "struts" filter in your web.xml:
    <filter>
        <filter-name>struts</filter-name>
        <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
        <init-param>
            <param-name>actionPackages</param-name>
            <param-value>com.company.newapp.webapp.action</param-value>
        </init-param>
    </filter>
    
  2. Add the Codebehind Plugin as a dependency in your pom.xml:
    <dependency>
        <groupId>org.apache.struts</groupId>
        <artifactId>struts2-codebehind-plugin</artifactId>
        <version>2.0.6</version>
    </dependency>
    
  3. Add a struts.codebehind.pathPrefix constant in struts.xml for your default pages directory:
    <constant name="struts.codebehind.pathPrefix" value="/WEB-INF/pages/"/>
    

That's it - now you can code away without configuring anything!

How does this compare to other web frameworks in AppFuse? Tapestry has a similar feature, but Spring MVC and JSF don't. Spring MVC still requires you create a bean definition for Controllers and JSF requires you write a chunk of XML for each managed bean. Of course, if you know how to do something similar with Spring MVC or JSF, please let me know.

Posted in Java at Mar 07 2007, 05:19:18 PM MST 9 Comments

Integrating Facelets and Ajax4JSF with MyFaces

I spent a few hours tonight integrating Facelets into an AppFuse-based application. The integrating was fairly easy thanks to the work Thomas Gaudin did back in January. For the most part, it was just a matter of 1) replacing <fmt:message> tags with #{text['key']} tags, 2) replacing the <%@ include file="/common/taglibs.jsp"%> at the top of each page with Facelet's namespace tags and 3) renaming the pages from *.jsp to *.xhtml. The only thing that tripped me up was I thought the "c" namespace was the same as JTLS's URI, but it's actually a whole new URI. Thanks to the Facelets developers for a much-needed fix for JSF.

After I got Facelets integrated and working, I dove into integrating Ajax4JSF. Two hours later and I have the simple repeater demo working. What took so long? I spent an hour staring at (and googling for) the solution to the following error:

PhaseListenerManager.informPhaseListenersBefore(74) | Exception in PhaseListener RENDER_RESPONSE(6) beforePhase.
java.lang.NullPointerException
        at com.sun.facelets.FaceletViewHandler.writeState(FaceletViewHandler.java:759)
        at org.ajax4jsf.framework.renderer.AjaxRendererUtils.writeState(AjaxRendererUtils.java:850)
        at org.ajax4jsf.framework.renderer.AjaxRendererUtils.encodeAreas(AjaxRendererUtils.java:740)
        at org.ajax4jsf.framework.renderer.AjaxContainerRenderer.encodeAjax(AjaxContainerRenderer.java:128)
        at org.ajax4jsf.ajax.UIAjaxRegion.encodeAjax(UIAjaxRegion.java:210)

The solution turned out to be removing the FaceletsViewHandler from faces-config.xml:

<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>

Also, it seems the following is required in web.xml:

    <context-param>
        <param-name>org.ajax4jsf.VIEW_HANDLERS</param-name>
        <param-value>com.sun.facelets.FaceletViewHandler</param-value>
    </context-param>

Hopefully this helps others googling for the exception above.

Posted in Java at Oct 12 2006, 02:41:24 AM MDT 9 Comments

Dependency Injection with SiteMesh

Let me start off by saying I think that both SiteMesh and Tiles are great frameworks. I was a long time user and fan of Tiles, and I think it's appropriate for certain situations. However, I've been a heavy user of SiteMesh since it passed the 10 minute test. While most heavy users of SiteMesh (the Atlassian guys come to mind) say that it can do everything that Tiles can do, these features are largely undocumented. This is my attempt to document a cool feature.

In a site I recently helped develop, we needed a couple of features:

  • A tabbed menu that highlighted the current tab based on which page you were on.
  • A bunch of "panels" on the right sidebar that changed according to the page.

To make this work, we used the meta tag functionality that SiteMesh provides.

Funny side/related note, I just googled for this tag and found this howto, which is similar to this one.

In our pages, we added the meta tags to set the active menu, as well as which panels to show in the sidebar:

<head>
    <title><fmt:message key="authorList.title"/></title>
    <meta name="menu" content="Authors"/>
    <meta name="panels" content="administration,blogs,events"/>
</head>

Then, in our decorator, we interpret these separately. First, we used Struts Menu (with Velocity) for the navigation system:

<c:set var="currentMenu" scope="request">
    <decorator:getProperty property="meta.menu"/>
</c:set>
<c:import url="/WEB-INF/pages/menu.jsp">
    <c:param name="template" value="/template/menu/tabs.html"/>
</c:import>

The menu.jsp page takes "template" as a parameter so we display the same menu links using a different Velocity template (for example, links at the bottom of the page).

<menu:useMenuDisplayer name="Velocity" config="${param.template}" permissions="rolesAdapter">

Then our tabs.html Velocity template uses the "currentMenu" attribute to determine which menu to highlight.

## displayMenu is defined in WEB-INF/classes/globalMacros.vm
#macro( menuItem $menu $level )
  #set ($title = $displayer.getMessage($menu.title))
  #if ($menu.url)
    #if ($menu.name == $currentMenu)
      <span class="current">
    #end
      <a href="$!menu.url" title="$title"><span>$title</span></a>
    #if ($menu.name == $request.getAttribute('currentMenu'))
      </span>
    #end
  #end
#end

#if ($displayer.isAllowed($menu))
    #displayMenu($menu 0)
#end

As far as the panel injection goes, that's processed using the following logic in our decorator:

<c:set var="panels"><decorator:getProperty property="meta.panels"/></c:set>
<!-- No panels set, use default set of panels -->
<c:if test="${empty panels}"><c:set var="panels" value="different,partners"/></c:if>
<c:forEach var="panel" items="${panels}">
    <c:import url="/WEB-INF/pages/panels/${panel}.jsp"/>
</c:forEach>    

Since this site used WebWork, the <ww:action> tag made it easy to give each panel independence. That is, each panel could load on its own, supply its own data, and not worry about the data being prepared beforehand. Here's an example:

<%@ include file="/common/taglibs.jsp"%>

<h2>Author Blogs</h2>

<ww:action name="'authors'" id="authors" namespace="default"/>

<div class="item">
    <ww:iterator value="#authors.authors" status="index">
        <a href="<ww:property value="blog.feedUrl"/>">
            <img src="${ctxPath}/images/icons/xml.gif" alt="XML Feed" 
                style="margin-right: 5px; vertical-align: middle"/></a>
        <a href="<ww:property value="blog.url"/>"><ww:property value="name"/></a>
        <br />
    </ww:iterator>
</div>

Of course, now that you can use Tiles with WebWork, Struts, Spring MVC and JSF - you could use Tiles for the injection and SiteMesh for the decoration.

Now if we could just get someone to write a JSF Decorator for SiteMesh, like Erik Hatcher did for Tapestry.

Posted in Java at Feb 16 2006, 09:57:23 AM MST 6 Comments

How to use Tiles with WebWork

This evening, I created a TilesResult for WebWork that allows you to use Tiles with WebWork. For the following to work in your application, you'll need a nightly build of Tiles, commons-digester (which Tiles requires) and this patch for WebWork. For your convenience, I've posted a patched webwork-2.2.2.jar (with TilesResult).

I also posted a webwork-tiles.war that you can try and download yourself. It's based on Equinox, so you will need to setup PostgreSQL and an "equinox" database - or you can just change the database settings in WEB-INF/lib/jdbc.properties.

On to the instructions:

1. In your web.xml file, you need to add a servlet entry for TilesServlet to load the tiles definitions into the ServletContext.

    <servlet>
        <servlet-name>tiles</servlet-name>
        <servlet-class>org.apache.tiles.servlets.TilesServlet</servlet-class>
        <init-param>
            <param-name>definitions-config</param-name>
            <param-value>/WEB-INF/tiles-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

2. In xwork.xml, use type="tiles" on your <result>.

    <action name="editUser" class="userAction" method="edit">
        <result name="success" type="tiles">userForm</result>
        <result name="input" type="tiles">userList</result>
    </action>

I'm sure WebWork has a way of making this result type the default, I just haven't found it yet.

Hat tip to Spring's TilesView (source) for showing how to make this work.

Update: While I'm a happy SiteMesh user, I've recently had some clients who were more interested in Tiles. This largely inspired me to see if WebWork + Tiles was possible.

Update 2: It looks like TilesResult will be included in WebWork 2.2.2. Now if we could just get the Tiles team to cut a release.

Posted in Java at Feb 16 2006, 01:08:42 AM MST 3 Comments

Using DWR with Spring and Hibernate

For the past few weeks, I've been developing an application using Struts, Spring, Hibernate and the DWR project for my XmlHttpRequest framework. As you might remember, I used JSON-RPC for Ajax stuff on my last project. I found DWR to be much more full-featured and easier to use. This post is meant to capture some issues I encountered so others won't have to jump the hurdles that I did. For those of you that get bored quickly, here's a movie (QuickTime) of the app's Ajax features.

I've been using version 0.4 of DWR, and I haven't had a chance to try out version 0.5. When I first started using it, I ran into a ThreadDeath problem that was easily resolved by changing a log.debug message to System.out.println. I tried to reproduce this issue yesterday and couldn't, so who knows what that was all about. As far as configuring DWR in your webapp, that's pretty easy to do, and well documented. See the project's documentation or this Spring MVC HowTo.

Here are a few things I remember from my development experience.

  • The examples are great, especially how to dynamically edit a table.
  • When developing, make sure to set the "debug" init-param to "true". This allows you to go to http://location:8080/yourapp/dwr and see a screen that allows you to call methods on your exposed classes.
  • In WEB-INF/dwr.xml, you need to specify a converter for each POJO you want to expose to your UI via JavaScript. I started out by converting a whole package, but found this to be *extremely* slow (we have a package of around 50 DTOs). So I changed it to be only the DTOs I was using. This turned out to take about 30 seconds to do the conversion, and was again unacceptable. The problem turned out to be that the converter was invoking all the lazy-loaded children for each DTO. My final solution was to create a NameValue object and only convert that. Then in my Spring bean, I populate it from DAOs and DTOs. I'm using Spring's OSIVF for Hibernate to ensure that DWR doesn't invoke lazy-loading.
  • I had to override a few of DWR's JavaScript functions in util.js b/c they didn't work for me. I changed showById() and toggleDisplay() to use style.display='' instead of style.display='block' b/c this is what I've always used and block doesn't work that well. I also changed useLoadingMessage() to have a cleaner-looking load message.
  • I used the Fade Anything Technique in this project and found that IE likes to have full 6-digit hex values for colors in CSS rules. The shorter 3-digit hex values simply don't work in IE.
  • Using "test" buttons that only showed up for my username proved to be a great way to test the UI and the Ajax stuff. These buttons called a number of JavaScript functions to drive the UI and wait between invoking different functions using window.setTimeout.

All in all, using DWR was a great experience and I definitely plan to use it more in my projects. The client loves the app - especially since it's wicked fast and seems to work like a desktop app.

Posted in Java at Apr 28 2005, 02:10:26 PM MDT 31 Comments

Use SiteMesh to decorate multiple webapps

Did you know it's possible to decorate multiple webapps with the same SiteMesh decorator? I learned how to do this from the SiteMesh mailing list:

  • Put sitemesh.jar in your container's classpath. You could put it into each webapp's WEB-INF/lib, but it looks like there's problems with that.
  • Create and deploy a webapp that contains the common decorator.
  • In each webapp's decorators.xml, add a "webapp" attribute to point to the webapp you deployed in Step 2.
    <decorators>
        <!-- load decorator from a different web-app deployed in the server -->
        <decorator name="main" webapp="some-other-webapp" 
            page="/decorators/main.jsp">
            <pattern>/*</pattern>
        </decorator>
    </decorators>
    

Try to do *that* with Tiles. ;-)

Posted in Java at Apr 12 2005, 10:05:19 AM MDT 12 Comments

Trim Spaces in your JSP's HTML

One of the annoying things about JSPs is all of the dynamic (non-rendered) parts of the page still produce line breaks. This means that if you do a view-source, you'll likely see large blocks of whitespace.

The good news is you can get rid of this whitespace if you're using Tomcat 5.5.x. Just locate the "jsp" servlet in $CATALINA_HOME/conf/web.xml and add the following <init-param>:

    <init-param>
        <param-name>trimSpaces</param-name>
        <param-value>true</param-value>
    </init-param>

I tested it and it works great. This begs the question - why isn't this on by default? Source: Struts Mailing List.

Update: JSP 2.1 adds the ability to trim whitespaces.

Posted in Java at Mar 23 2005, 10:24:58 PM MST 39 Comments

Ajax webapps are cool, but non-javascript versions still needed

I think we can all probably learn a lesson from Google. I've heard that GMail is the "gold standard" for Ajax applications. If that's the case, then you should note that they've recently added a "basic HTML" link to the bottom of their pages. With this link, you can view your e-mail using the old way: Yahoo-style, no-JavaScript-needed. My guess is they added it because of demand, or simply to compete with other providers who have this feature. I think it's a good lesson though: use Ajax features in webapps where appropriate, but don't make JavaScript necessary to use your app.

A couple of Ajax features I've been thinking of developing:

  • Saving forms with XMLHttpRequest: just display a success message at the top, and switch the "Cancel" button to "Done". Since the form's content doesn't change, this seems like a reasonable use of the technology.
  • Switching out entire "content" <div> elements. Most of my apps have a <div id="content">, so it'd probably pretty easy to just replace that in response to button and link clicks. Of course, the hard part is having the requested server-side object load the view template, process it, and send back the content. This is probably more trouble than it's worth.

Posted in Java at Mar 07 2005, 07:42:18 AM MST 13 Comments

Simplifying XmlHttpRequest with JSON-RPC

In my day job, we decided to use a little XMLHttpRequest lovin' to populate one drop-down from another. This is my review of JSON-RPC, an open source JavaScript library and servlet for simplifying XMLHttpRequest. I considered integrate Direct Web Remoting (DWR) as well, but its java.net site was down the day I needed it. I started out with JSON-RPC 0.7, which caused some conflicts with Commons Validator client-side validation. This was fixed in the 0.8 release. JSON-RPC takes a little more setup than I care for, but it's pretty easy nonetheless:

  1. Download the 0.8 release from http://oss.metaparadigm.com/jsonrpc-dist/json-rpc-java-0.8.tar.gz.
  2. Add the JAR to your project and the webapps/jsonrpc/jsonrpc.js to your projects' "scripts" folder. Include this file in your SiteMesh decorator or Tiles layout. If you're not using SiteMesh or Tiles, it's high time you started.
  3. JSON-RPC currently requires that you register each class you want call methods on. In our project, I registered a Spring bean (LookupHelper) that's a singleton with references to Maps in the ServletContext. Then we used JavaScript functions to call JSON-PRC and look up units for a plant, and vice versa. I'm not going to put the LookupHelper class here - you'll have to trust its methods return a single String or a comma-separated list of Strings. To register this bean with JSON-RPC, I created an HttpSessionListener and configured it in web.xml.

    /**
     * UserListener class used to add/remove session attributes when
     * a user first logs in.  Mainly for JavaScript Remote Scripting stuff.
     *
     @author Matt Raible
     */
    public class UserListener implements HttpSessionListener, HttpSessionAttributeListener 
        private final Log log = LogFactory.getLog(UserListener.class);
        public final static String BRIDGE_KEY = "JSONRPCBridge";

        /**
         * Initializes LookupHelper singleton with values needed for lookup
         *
         @param event the HttpSessionEvent to grab session information from
         */
        public void sessionCreated(HttpSessionEvent event) {
            // Find the JSONRPCBridge for this session or create one
            // if it doesn't exist. Note the bridge must be named BRIDGE_KEY
            // in the HttpSession for the JSONRPCServlet to find it.
            HttpSession session = event.getSession();
            JSONRPCBridge jsonBridge = new JSONRPCBridge();
            jsonBridge.setDebug(true);
            session.setAttribute(BRIDGE_KEY, jsonBridge);
        }

        /**
         * Destroys LookupHelper
         *
         @param event the HttpSessionEvent to grab session information from
         */
        public void sessionDestroyed(HttpSessionEvent event) {
            if (event.getSession() != null) {
                event.getSession().removeAttribute(BRIDGE_KEY);
            }
        }

        public void attributeAdded(HttpSessionBindingEvent event) {
            if (event.getName().equals(BRIDGE_KEY)) {
                HttpSession session = event.getSession();
                // register LookupHelper so we can call methods on it
                ApplicationContext ctx =
                        WebApplicationContextUtils
                        .getWebApplicationContext(session.getServletContext());

                // check for null so we don't have to initialize Spring in tests
                if (ctx != null) {
                    log.debug("Registering lookupHelper for XmlHttpRequest...");
                    JSONRPCBridge jsonBridge =
                        (JSONRPCBridgesession.getAttribute(BRIDGE_KEY);
                    jsonBridge.registerObject("lookupHelper",
                                              ctx.getBean("lookupHelper"));
                }
            }
        }

        public void attributeRemoved(HttpSessionBindingEvent event) {
            // don't care
        }

        public void attributeReplaced(HttpSessionBindingEvent event) {
            // same as attribute added
            attributeAdded(event);
        }
    }

  4. After this setup was complete, I was able to add the following JavaScript to the bottom of my JSP. These are functions that our drop-downs call to populate each other, and keep their options in synch.

    <script type="text/javascript">
    var jsonurl = "${ctx}/jsonrpc";
    var jsonrpc = null;
    var unitDropDown = document.getElementById("equipmentName");

    function filterUnits(plantDropDown) {
        var plantName = plantDropDown.options[plantDropDown.selectedIndex].value;

        if (plantName == "") {
            reloadUnits("");
            return;
        }

        try {
            jsonrpc = new JSONRpcClient(jsonurl);
        catch(e) {
            alert(e);
        }

        // Call a Java method on the server
        var units = jsonrpc.lookupHelper.getUnitsForPlant(plantName);
        setUnits(units);
    }

    function reloadUnits(value) {
        if (value == "") {
            try {
                jsonrpc = new JSONRpcClient(jsonurl);
            catch(e) {
                alert(e);
            }

            // Call a Java method on the server
            var units = jsonrpc.lookupHelper.getAllUnits();
            setUnits(units);
        }
    }

    function setUnits(units) {
        var unitArray = units.split(",");

        unitDropDown.options.length = 1;  // keep "All" option
        for (i=0; i < unitArray.length; i++) {
            unitDropDown.options[unitDropDown.options.length=
                new Option(unitArray[i], unitArray[i]);
        }
    }
    </script>

The hardest part of using JSON-RPC is setting it up. We only experienced minor issues with Commons Validator, but since the JSON-RPC 0.8 release - everything has worked great, on all browsers we need to support. The only thing I don't like about this library is that you have to register objects for each user's session. I briefly looked at DWR and it looks a little cleaner - especially b/c of its Spring integration. The next time we need XMLHttpRequest, we'll probably use DWR just to compare the two.

Posted in Java at Mar 03 2005, 08:44:40 AM MST 8 Comments

Edit Screens with JSF

I'm working with JSF this morning and I'm finding one thing particularly annoying. I'm working on a simple master/detail screen and I'm tweaking the detail screen to fit my needs. It's just a <form> with some form elements. I change something, run "ant deploy-web" and hit "refresh" to see my updated page. Since everything in JSF is a post, I get prompted to re-submit the form. Fine, I agree - then I'm returned to the list screen. Argggh - why can't I just view the form again?! This might just be a MyFaces nuance, I'm not sure. Anyone know of a workaround?

Wanna see the bug/feature in action? Go to http://demo.raibledesigns.com/equinox-jsf/userList.html, click on a row - and after the edit screen displays, hit refresh on your browser. In an ideal world, you'd see the form again, but nope - you get the list instead.

Posted in Java at Nov 28 2004, 10:02:25 AM MST 9 Comments