DigitalJoel

2011/02/05

Using Mockito To Test Spring MVC Ajax Interaction

Filed under: java, spring, testing — Tags: , , , , , , , — digitaljoel @ 4:36 pm

So, I shared in Ajax Post to Spring MVC Controller what I learned about making an ajax post to a Spring MVC Controller. Then I shared in Mock Testing Spring MVC Controller what I learned about using Mockito to test my Spring MVC controller. So what about testing my RequestHandler that handles the ajax post and returns a JSON object? Well, as Samuel L. Jackson says in Jurassic Park, “Hold on to your butts”

Here’s the method that handles the ajax post of form data.

    @RequestMapping( value="answer/new", method=RequestMethod.POST)
    public ResponseEntity<String> newAnswer( @RequestParam(value="answerSeverity", required=true ) String severity
            , @RequestParam( value="answerText", required=true ) String text
            , @RequestParam( value="requiresReason", required=false, defaultValue="false" ) boolean requiresReason
            , @RequestParam( value="answerReasons", required=false ) List<Long> reasonKeys
            )
    {
        Severity sev = Severity.valueOf( severity );
        SurveyAnswer answer = new SurveyAnswer( text, sev );
        answer.setRequiresReason( requiresReason );
        if ( requiresReason )
        {
            // add all the reasons
            List<SurveyAnswerReason> reasons = surveyService.findReasonsByKey( reasonKeys );
            for( SurveyAnswerReason reason : reasons )
            {
                answer.addReason( reason );
            }
        }
        answer = surveyService.persist( answer );
        this.getAnswers( sev ).add( answer );
        return createJsonResponse( answer );
    }

    private ResponseEntity<String> createJsonResponse( Object o )
    {
        HttpHeaders headers = new HttpHeaders();
        headers.set(  "Content-Type", "application/json" );
        String json = gson.toJson( o );
        return new ResponseEntity<String>( json, headers, HttpStatus.CREATED );
    }

You can read the previous post for information on what’s going on here, but basically, we handle the form post, create a new SurveyAnswer, and then return the created answer as a JSON object using the createJsonResponse method.

In order to mock test this, I’m going to have to mock all the calls to the surveyService methods. That would be findReasonsByKey, and persist. The persist was a bit tricky because I wanted it to just return the answer that was passed as an argument to ensure that the controller was creating the answer correctly. Here’s the code to do that.


        when( surveyService.persist( any( SurveyAnswer.class ))).thenAnswer(
                new Answer<SurveyAnswer>()
                {
                    @Override
                    public SurveyAnswer answer( InvocationOnMock invocation ) throws Throwable
                    {
                        Object[] args = invocation.getArguments();
                        return (SurveyAnswer) args[0];
                    }
                });
        when ( surveyService.findReasonsByKey( anyCollectionOf( Long.class ))).thenReturn( getReasons() );

I put it in my @Before annotated setup method in my unit test. I didn’t come up with it myself, I adapted it from an excellent answer to this question on StackOverflow.com. That snippet allows me to just return the argument passed to the method, which is basically what JPA would do, other than setting the key and version, which I don’t really need for my test anyway. The mocked out findReasonsByKey just returns a list of objects that I’m creating elsewhere for testing purposes only.

So, on to the test. Here’s the code:


    @Test
    public void testNewAnswerWithReasons()
    {
        ResponseEntity<String> response = controller.newAnswer( answerSeverity.name(), answerText,
                true, getReasonKeys() );
        assertEquals( "application/json", response.getHeaders().get( "Content-Type" ).get( 0 ));
        SurveyAnswer answer = gson.fromJson( response.getBody(), SurveyAnswer.class );
        assertEquals( getSingleAnswerWithReasons(), answer );
    }

There are some helper methods that create the object graph needed for the SurveyAnswer. It then calls the method on the controller (which is also initialized in the @Before setup method) and checks the result. I’m really looking for two things. First, that the response has the Content-Type set correctly to application/json, and second, that I get an answer that corresponds to the values I passed in. Here again, I use Google’s GSON library for converting from my JSON string to my Java object. Once that is done, I can just test for equality with the answer I’m expecting. Obviously, for that to work, you’ll need to make sure your equals method is correct, but that’s an issue well addressed elsewhere on the internet and well beyond the scope of this post.

Mock Testing Spring MVC Controller

Filed under: development, spring, testing — Tags: , , , , , , — digitaljoel @ 2:08 pm

I’m in the midst of implementing a Spring MVC based web application. We have hudson for continuous integration builds that run all of our unit tests, but testing spring MVC controllers still just isn’t quite as easy as I would hope. There is some information on testing the controllers in the official spring documentation, but for someone like me that’s not a spring guru, or just starting out, it wasn’t enough to get me going. I recently was introduced to Mockito, so I spent a bit of time today trying to get a test for our controller using Mockito. It was simple and took no time at all.

I have yet to try it in a more complex controller method, but I think it’ll work just fine , especially when I get some utilities in place to initialize the mock objects that are commonly used by the controller. As it stands, here’s what I did to get it going. This tests the controller as a POJO, without using any spring configuration or capabilities.

Here is the controller I wish to test. Obviously I stripped out a bunch of code not needed for this demonstration.

@Controller
@RequestMapping( "/admin/survey" )
public class SurveyAdminController
{
    
    @Resource
    private SurveyService surveyService;
    
    @Resource
    private UnitService unitService;
    
    @Resource
    private OrganizationService orgService;
    
    /**
     * Show the table of existing questions to the user
     * @param model
     * @return
     */
    @RequestMapping( "questions" )
    public ModelAndView listQuestions()
    {
        ModelAndView mav = new ModelAndView( "/admin/questionList");
        List<SurveyQuestion> questions = surveyService.findAllQuestions();
        mav.addObject( "questions", questions );
        List<UnitFeature> features = unitService.getAllFeatures();
        mav.addObject( "features", features );
        List<Organization> organizations = orgService.getAll();
        mav.addObject( "organizations", organizations );
        return mav;
    }
}

For this simple test, it’s just going to validate that the view name is correct, and that the model that’s returned contains the correct information.


package com.bi.controller;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;

import java.util.ArrayList;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.servlet.ModelAndView;

import com.bi.data.OrganizationService;
import com.bi.data.SurveyService;
import com.bi.data.UnitService;
import com.bi.data.corp.Organization;
import com.bi.data.survey.SurveyQuestion;
import com.bi.data.unit.UnitFeature;
import com.bi.web.util.MessageUtil;

public class SurveyAdminControllerTest
{
    @Mock SurveyService surveyService;
    @Mock UnitService unitService;
    @Mock OrganizationService orgService;
    @Mock MessageUtil messageUtil;

    @Before
    public void setup()
    {
        // this must be called for the @Mock annotations above to be processed.
        MockitoAnnotations.initMocks( this );
    }
    
    @Test
    public void testListQuestions()
    {
        // setup our mock question list
        List<SurveyQuestion> questions = new ArrayList<SurveyQuestion>();
        questions.add( new SurveyQuestion( "asdf", null ));
        when( surveyService.findAllQuestions()).thenReturn( questions );

        // setup our mock feature list
        List<UnitFeature> features = new ArrayList<UnitFeature>();
        features.add( new UnitFeature( "TEST FEATURE" ));
        when( unitService.getAllFeatures()).thenReturn( features );

        // setup our mock organization list
        List<Organization> orgs = new ArrayList<Organization>();
        orgs.add( new Organization( "TEST ORGANIZATION" ));
        when( orgService.getAll()).thenReturn( orgs );

        // create an instance of the controller we want to test
        SurveyAdminController controller = new SurveyAdminController();

        // since we aren't using spring, these values won't be injected, so set them manually
        ReflectionTestUtils.setField( controller, "surveyService", surveyService );
        ReflectionTestUtils.setField( controller, "unitService", unitService );
        ReflectionTestUtils.setField( controller, "orgService", orgService );

        // call the method under test
        ModelAndView mav = controller.listQuestions();

        // review the results.
        assertEquals( questions, mav.getModel().get( "questions" ));
        assertEquals( features, mav.getModel().get( "features" ));
        assertEquals( orgs, mav.getModel().get( "organizations" ));
        assertEquals( "/admin/questionList", mav.getViewName());
    }
}

The comments should be pretty self explanatory in the test class. The awesomeness of Mockito is how you setup the mocks. A line like this:

        when( unitService.getAllFeatures()).thenReturn( features );

reads very nicely and sets up the return value for my service object, which keeps me from needing a database or anything setup in order to test my controller method. Mockito ftw.

2011/01/29

Ajax Post to Spring MVC Controller

Filed under: development, java, jquery, spring — Tags: , , , , , — digitaljoel @ 7:38 pm

I wanted to submit an html form to my Spring MVC Controller, but I wanted to do it with ajax. I had previously submitted a single value and returned a JSON object for use in jquery, but I had yet to do it with an entire form. I’m a java guy, so there may be better ways to do the html stuff, but this is how I did it and thought I would share some of what I learned along the way.

First, here’s my form.

    <div id='newAnswerDialog'>
        <form id='newAnswerForm' name='newAnswerForm' action='/admin/survey/answer/new' onsubmit='return false;' method='post'>
            <label for='severity'><spring:message code='input.answer.severity' text='Severity' /></label>
            <select id='answerSeverity' name='answerSeverity'>
                <option value='MINIMAL'>MINIMAL</option>
                <option value='MINOR'>MINOR</option>
                <option value='MODERATE'>MODERATE</option>
                <option value='SEVERE'>SEVERE</option>
                <option value='URGENT'>URGENT</option>
            </select><br/>
            <label for='answerText'><spring:message code='input.answer.text' text='Answer Text' /></label>
            <input type='text' id='answerText' name='answerText'/><br/>
            <label for='requiresReason'><spring:message code='input.answer.requiresReason' text='Requires Reason?' /></label>
            <input type='checkbox' id='requiresReason' name='requiresReason' onclick='toggleReasonControls(this)'/><br/>
            <label for='answerReasons'><spring:message code='input.question.reasons' text='Reasons' /></label>
            <select class='newReason' id='answerReasons' name='answerReasons' multiple='multiple'>
                <c:forEach items='${reasons}' var='reason'>
                    <option value='${reason.key}'>${reason.text}</option>
                </c:forEach>
            </select><br/>
            <input type='button' onclick='createNewAnswer()' value='<spring:message code='submit' />'/><input type='button' onclick='cancelNewAnswer()' value='<spring:message code='cancel' />' />
        </form>
    </div>

It’s JSP using the JSTL and the Spring tag libraries, but it’s all basically html. You can see that I have some javascript in the requriesReason checkbox that is called when it is clicked. The javascript function just enables everything with a css class of newReason based on the state of the requiresReason checkbox. This will come into play later.

When the user clicks the button labeled submit, it calls the javascript function called “createNewAnswer()” which looks like this


        function createNewAnswer()
        {
            $.post( '<c:url value='/admin/survey/answer/new' />'
                    , $('#newAnswerForm').serialize()
                    , function( data )
                    {
                        // add the option to the list of answers, and select it.
                        var options = $('#'+data.severity.toLowerCase()+'Answer').attr( 'options' );
                        options[options.length] = new Option( data.text, data.key, true, true );
                        $('#newAnswerDialog').dialog( 'close' );
                    }
                    , 'json' );
        }

This uses the jquery post function to submit the values to the server and read the response.

The first parameter is the URL to submit to. I’m using the JSTL c:url tag so that the web application context is added to the url. Your controller method must be configured to accept POST requests since we are submitting via POST, not GET. I’ll show the controller implementation later on.

The second parameter is the data you want to submit to the Controller. This is where I found it a bit tricky. Getting the form is simple with jquery. Getting the data is also simple using jquery’s serialize() method. The trick is in the fine print in the serialize method. You can find the documentation here. At the time of this writing, it says:

Note: Only “successful controls” are serialized to the string. No submit button value is serialized since the form was not submitted using a button. For a form element’s value to be included in the serialized string, the element must have a name attribute. Data from file select elements is not serialized.

It then links to this page that has an explanation of what a “successful control” is. The gist of it is that the control cannot be disabled. The control must have a name. Checkboxes that are not checked may not be submitted. At least, those are the parts that affected me when trying to get this to work.

So, knowing that, it affects how I create the controller method to handle the post. Here’s the implementation:

    @RequestMapping( value='answer/new', method=RequestMethod.POST)
    public ResponseEntity<String> newAnswer( @RequestParam(value='answerSeverity', required=true ) String severity
            , @RequestParam( value='answerText', required=true ) String text
            , @RequestParam( value='requiresReason', required=false, defaultValue='false' ) boolean requiresReason
            , @RequestParam( value='answerReasons', required=false ) List<Long> reasonKeys
            )
    {
        Severity sev = Severity.valueOf( severity );
        SurveyAnswer answer = new SurveyAnswer( text, sev );
        answer.setRequiresReason( requiresReason );
        if ( requiresReason )
        {
            // add all the reasons
            List<SurveyAnswerReason> reasons = surveyService.findReasonsByKey( reasonKeys );
            for( SurveyAnswerReason reason : reasons )
            {
                answer.addReason( reason );
            }
        }
        answer = surveyService.persist( answer );
        return createJsonResponse( answer );
    }

Notice that the requiresReason, and answerReasons are marked as optional. This is because they may or may not be passed. If you have a problem with your mapping here, you may get a 400 error with a message that says, “The request sent by the client was syntactically incorrect ()” if you look at it in the XHR response. That’s what I was getting from tomcat. Once I set the required to false on the optional attributes, things went through much better.

The rest of the code is just for creating our entity and saving it. You can do whatever you want with the data you post. The next critical part is in the createJsonResponse method. You’ll notice that the controller method doesn’t return a String for the view, and it doesn’t return a ModelAndView. It is returning a ResponseEntity, which I create in the createJsonResponse method, which is as follows:


    private ResponseEntity<String> createJsonResponse( Object o )
    {
        HttpHeaders headers = new HttpHeaders();
        headers.set(  'Content-Type', 'application/json' );
        String json = gson.toJson( o );
        return new ResponseEntity<String>( json, headers, HttpStatus.CREATED );
    }

It’s a very simple method that just creates the ResponseEntity and sets the headers so the javascript receiving the response gets json as it is expecting (see the last parameter in the $.post method, it says we are expecting json in return.)

I am using Google’s JSON library to convert my entity object to a JSON object that I can return to the view. With that conversion, I can use the field names in the java object to reference the values in the returned JSON object. It’s really quite slick.

So, now we’ve done the work on the server and returned the response to the browser. The third argument to the $.post jquery method is a function that is called on successful return of the json object from the server. Here is my function again:

                    function( data )
                    {
                        // add the option to the list of answers, and select it.
                        var options = $('#'+data.severity.toLowerCase()+'Answer').attr( 'options' );
                        options[options.length] = new Option( data.text, data.key, true, true );
                        $('#newAnswerDialog').dialog( 'close' );
                    }

I’m taking the JSON object I receive and adding a new option to a select box further down in the page, and marking that option as selected. As I said above, I’m referencing the properties of the JSON object using the field names in the Java object.

There is a way to get a javascript method called when the request fails, but it’s not built into jquery’s post method, and I haven’t taken the time go through that part yet.

So, there you have it. A fair amount of time of trial and error all summed up in about 1000 words, including code.

2011/01/15

Simple Application Configuration With Spring

Filed under: development, java, spring — Tags: , , — digitaljoel @ 1:48 am

Inevitably it happens in every project. You’re coding along, and you have a “constant” that isn’t really constant. In my case, it was the number of minutes that should pass before a given token is invalid. 2 hours? Yeah, that sounds good… for now. But you feel all dirty if you put it right in a .java file. You could provide the value as a property to your bean if you are doing XML configuration, but is that really that much better? You want it in a properties file, because that’s one reason properties files exist, and it’s all configurable and stuff, right?

The company at which I spend my daylight hours has a ConfigurationService that takes care of hierarchical property overriding and all that. I’m sure it’s nifty. I didn’t want to write anything like that for the project on which I spend my evening/weekend hours, so I found something in spring that would do it for me and had it going pretty quickly.

Alright, here we go with the simple solution built right into Spring.

My project is a multi-module maven project with a single parent pom and each module under that parent project. I believe it’s a fairly common setup. In a module early in the dependency tree, I created a spring configuration file that looks like this.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <!-- a bean for storing configuration properties. -->    
    <bean id="runtimeConfig" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
        <property name="locations">
            <list>
                <value>classpath*:/META-INF/config/*.properties</value>
                <value>file:///${user.home}/myproject.properties</value>
            </list>
        </property>
    </bean>
    
</beans>

Yep, one bean. It creates a single java Properties object from the list of files I give it, from top to bottom, with properties in later files overriding properties in earlier files. In this case, I added the wildcard /META-INF/config/*.properties, so all of my other modules can put their default property values in properties files in META-INF/config and if they depend on this module, it’ll find them. Note that you must use classpath*: and the path must have at least one directory in it or else you will likely read from the location in only one of your jar files.

Some considerations here:

If you are using another jar, say from a third party vendor, and they have properties in META-INF/config, then you may load all those properties too. I haven’t seen it in my limited testing, but that would stink. You might be better off changing that to something like META-INF/config/myproject-*-default.properties, then you would be more sure that you will only get your properties. You don’t really want to put specific properties file names in here because you don’t want this module, which is early in the dependency tree, to know explicitly about properties files in modules it knows nothing about.

Also, depending on how your properties files within your jar files are named, your property overriding may not occur as you hoped. Hopefully, you don’t have properties with the same name in different properties files, in which case this is a non-issue. If you do have properties with the same name in different properties files in your various modules you may have to be even more careful in how they are loaded in your bean definition.

Finally, with the file:///${user.home}/myproject.properties value last, you can override any of the properties in the default configuration files with properties in a single file in your user home directory. This is pretty cool. This way your application can define the default timeout at 120 minutes but when you are testing and don’t want to wait 2 hours for a timeout you just set the value in your home directory configuration file to 1 minute and Bob’s your uncle. No risk of checking in that configuration value because it’s not under source control.

Another thing you could likely do here is to load different properties files based on some environment variable. You want to test against postgres? Set an environment property when you run your build and set the pattern to something like file:///${user.home}/${dbtype}-config.properties. Each ${dbtype}-config.properties file could give connection information for the specified database, so your home directory would have postgres-config.properties, oracle-config.properties, mysql-config.properties, etc. Then, in hudson, bamboo, or whatever you define a property for the build that has the database type in it. For maven command line, your command would be something like this. I tried it with a simple property and it worked great.

mvn -DargLine="-Ddbtype=postgres" test

Ok, moving on. Now we want to use this fancy properties object we’ve created.

So, we’ve moved down the dependency tree to a module that depends on my module with the fancy runtimeConfig bean. In my spring configuration within that module, I do the following:

<!-- all that other spring stuff you have defined -->


    <import resource="classpath:/META-INF/spring/domain-api-beans.xml"/>
    <context:property-placeholder properties-ref="runtimeConfig" />

    <bean id="yourDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

So, we import our previous spring configuration file so we can use the awesome runtimeConfig bean. Next, we create a property-placeholder so we can reference the properties in our runtimeConfig within this very same configuration file. Then, magically, all of the ${…} values in our datasource definition are replaced by the values in our properties files.

“BUT WAIT!” You say, “This isn’t the use case you mentioned at the top of the post. I can do this by specifying the locations of the property-placeholder.” Yes. Yes you can. This was just extra credit to show that we don’t have to point to the same locations in two definitions. Just once will do.

So, now we have a service or some bean or something, and we want to get our expiration configuration value out of it. How would we accomplish such a thing? Funnily enough, that’s the easiest part of all this.


    // all my service bean code or whatever

    // here we read the value from the runtimeConfig bean using Spring's expression language.
    @Value("#{runtimeConfig['account.validation.expiration.minutes']}" )
    private long minutes;

    // here's a nice method to return the value so I don't have that ugly @Value thing everywhere I want to reference the value.
    public long getValidityMinutes()
    {
        return minutes;
    }
    
    // the rest of my service bean code and stuff

Voila, I’ve successfully read the property from the file in my user home, and if it’s not set there, then I’ve read it from the reasonable defaults set within the properties file in the jar that uses the value. I didn’t have to write any service, bean, or anything to implement a hierarchical properties based configuration system for my application.

Update: One thing I failed to mention, which you probably already figured out, is that since the runtimeConfig is just a bean, if you know that you are going to be reading a ton of properties out of it, you could just inject the entire bean, something like this.

@Resource( "runtimeConfig" )
private Properties config;

2010/12/28

Spring MVC and JSR-303 Validation Groups

Filed under: development, java, spring — Tags: , , , , , , — digitaljoel @ 8:27 pm

@Valid. That wonderful little annotation. In my Spring controller I do something like this.

    @RequestMapping( value="/editAccount", method=RequestMethod.POST )
    public String postEditAccount( Model model, HttpServletRequest request, 
            @Valid AccountInfo info, BindingResult infoResult )

and everything is great. I know that all of the JSR-303 annotations I’ve put on my AccountInfo bean have been validated and the data is all correct and good. Let’s say that my AccountInfo bean looks something like this.

public class AccountInfo
{

    @NotNull
    private String username;
    @NotNull
    private String newUsername;
    @NotNull
    private String confirmNewUsername;
    
    @NotNull
    private String password;
    @NotNull
    private String newPassword;
    @NotNull
    private String confirmNewPassword;
    
    @NotNull
    private String firstName;
    @NotNull
    private String lastName;
    @NotNull
    private String phone;
    private String fax;
    
    // constructors, getters, setters, etc. down here
}

Suddenly, everything isn’t so hunky dory anymore. Fortunately for us, JSR-303 has a great mechanism for only validating some portion of the object. It’s known as validation groups. There’s plenty of information out there on them, so I’ll give you the extreme Reader’s Digest version. Basically, you specify a list of marker interfaces in your validation annotations, and then when you call the validator, you can also pass in a list of the marker interfaces that you would like to validate against. Ok, that sentence doesn’t make much sense unless you already know about groups. So, here’s a new version of AccountInfo that demonstrates.


public class AccountInfo
{

    @NotNull( groups={ChangeUsername.class} )
    private String username;
    @NotNull( groups={ChangeUsername.class} )
    private String newUsername;
    @NotNull( groups={ChangeUsername.class} )
    private String confirmNewUsername;
    
    @NotNull( groups={ChangePassword.class} )
    private String password;
    @NotNull( groups={ChangePassword.class} )
    private String newPassword;
    @NotNull( groups={ChangePassword.class} )
    private String confirmNewPassword;
    
    @NotNull
    private String firstName;
    @NotNull
    private String lastName;
    @NotNull
    private String phone;
    private String fax;
    
    // constructors, getters, setters, etc. down here
    
    public interface ChangePassword {};
    public interface ChangeUsername {};

}

Now we’ve told the validator that when we run the validator without any groups, we want it to validate firstName, lastName, and phone. If you don’t specify any groups, they get the Default.class group. BUT, if we run the validator passing in the AccountInfo.ChangePassword.class, then it will only validate password, newPassword, and confirmNewPassword. If we want to do both, then we can pass in AccountInfo.ChangePassword.class AND Default.class and it will validate both groups. That’s awesome sauce right there. Now, we can use this same backing bean in the page where the user is created which contains all the fields, we can use it in the edit account info page which only has the stuff validated by Default, we can use it in our change password page, and we can also use it in our change username page, and in each case, we only validate the portions that we are concerned about for those pages. One bean for all four pages.

With that worked out, we should be able to just add the groups to our @Valid annotation, right? Nope. Wait, what? All that work to put in validation groups and we can’t even use them with the JSR-303 sanctioned validation annotation? Yep, that’s right. There’s an improvement in Spring’s Jira to add a new @Valid annotation that will allow you to specify groups, but until that happens, you’ll have to run the validator yourself.

I think it sound worse than it is. As you can see in the controller method I put at the start of this post, that after each @Valid annotated object, you need to have the BindingResult in order to see the errors. Then, in your controller method you have to check the BindingResult in order to see if there are errors, if there are, do something, if not, do something else. So, how is that any different than having to just run the check yourself? Here’s what I did.


    /**
     * Test validity of an object against some number of validation groups, or
     * Default if no groups are specified.
     * 
     * @param result Errors object for holding validation errors for use in
     *            Spring form taglib. Any violations encountered will be added
     *            to this errors object.
     * @param o Object to be validated
     * @param classes Validation groups to be used in validation
     * @return true if the object is valid, false otherwise.
     */
    private boolean isValid( Errors result, Object o, Class<?>... classes )
    {
        if ( classes == null || classes.length == 0 || classes[0] == null )
        {
            classes = new Class<?>[] { Default.class };
        }
        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        Set<ConstraintViolation<Object>> violations = validator.validate( o, classes );
        for ( ConstraintViolation<Object> v : violations )
        {
            Path path = v.getPropertyPath();
            String propertyName = "";
            if ( path != null )
            {
                for ( Node n : path )
                {
                    propertyName += n.getName() + ".";
                }
                propertyName = propertyName.substring( 0, propertyName.length()-1 );
            }
            String constraintName = v.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName();
            if ( propertyName == null || "".equals(  propertyName  ))
            {
                result.reject( constraintName, v.getMessage());
            }
            else
            {
                result.rejectValue( propertyName, constraintName, v.getMessage() );
            }
        }
        return violations.size() == 0;
    }

Alright, it’s a pretty simple method, but we’ll walk through it.


        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        Set<ConstraintViolation<Object>> violations = validator.validate( o, classes );

Here we get the validator instance and get the set of violations back. This is based on the validation groups that were passed in, or Default if there were none passed. I believe I read somewhere that you can actually get the Validator injected by Spring, but I haven’t played with it yet to find out. If you do and it works, let me know!

Next is the part where we take the JSR-303 validations and map them to Spring form errors.


            Path path = v.getPropertyPath();
            String propertyName = "";
            if ( path != null )
            {
                for ( Node n : path )
                {
                    propertyName += n.getName() + ".";
                }
                propertyName = propertyName.substring( 0, propertyName.length()-1 );
            }

We get the property name of the violation, which will hopefully map to the “path” in the spring input tag you are using. I haven’t tested this on anything with any depth (for instance, if your bean contains an object that also has validation annotations on it) so I’m not sure how it’ll work there. Once again, if you find out, leave a comment. Anyway, now that we have the property name, we can use that later on to tell Spring which field failed validation so the correct errors field can be shown.


            String constraintName = v.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName();

Now we get the name of the constraint that failed. In all cases above, it would be NotNull. If you annotation is something like @Size( min=85 ) then the constraint would be Size. We use this so we can get error messages mapped the same way spring binding violations do, so if you are using custom messages in your messageSource for your fields or constraint messages, then this should work just the same.


            if ( propertyName == null || "".equals(  propertyName  ))
            {
                result.reject( constraintName, v.getMessage());
            }
            else
            {
                result.rejectValue( propertyName, constraintName, v.getMessage() );
            }

Finally, before returning the true or false, we have to add the violations to the Errors object. If we have a propertyName, then that means it’s a field error. If we don’t, it’s a global object error, which ought to happen if you use a class level validation annotation instead of a field level validation annotation. Yep, I’m going to say it one more time. I haven’t tested that yet, but it won’t be long before I do since I’ve got to get a class level validator to ensure the newPassword and confirmNewPassword fields contain the same value.

So, that’s a lot of explaining for a single method, but hopefully it shows that adding this validation really isn’t that much more difficult than checking the BindingResult that Spring gives back to you after the default @Valid processing. In places where I don’t have to use validation groups, I’m obviously still going to use the @Valid annotation as is, and if Spring gives me a new annotation I can use to run validation groups, I’ll jump to that and rip this out right away. BUT, until then, this will have to do.

Finally, here’s how I call that method in my controller.


    @RequestMapping( value="/editAccount", method=RequestMethod.POST )
    public String postEditAccount( Model model, HttpServletRequest request, 
            AccountInfo info, BindingResult infoResult )
    {
        if ( !isValid( infoResult, info, AccountInfo.ChangePassword.class, AccountInfo.ChangeUsername.class ))
        {
            return "editAccount";
        }
        // otherwise we process the form and do stuff.
    }

Obviously, you would want to move the isValid method out of this controller and into something that can be shared between Controllers.

2010/12/09

JSP Date Formatting

Filed under: development, java — Tags: , , , — digitaljoel @ 10:58 am

I had an input field that took a date type as mentioned in my previous post. Now the problem was, while I input it in the form MM/dd/yyyy, when I was displaying it on the page for the user to modify, it would come back with time, timezone, all sorts of crap definitely NOT in MM/dd/yyyy format. This caused the form submission to fail unless the user corrected the field every time because date conversion would fail with the huge, lame format.

One wrinkle was that I wanted to support internationalization. While our application currently doesn’t have anything other than en_US right now, I want to make sure I am getting the right date pattern for all instances. So, this is what I did.

<label id="birthday_label" for="birthday" title="<spring:message code="user.birthday.alt" />">
    <spring:message code="user.birthday" />
</label>

<spring:message code="dateFormat" var="dateFormat" />

<input id="birthday" name="birthday" type="text" 
        value="<fmt:formatDate value="${reg.birthday}" 
        type="date" pattern="${dateFormat}" />" />

I have my spring message bundle available, and in that bundle, I have a field called dateFormat which contains the string MM/dd/yyyy. It’s also the field used for doing the date conversion on input. I needed to get the format for use in the fmt:formatDate tag, which is given as the value of the input tag. I’m not a fan of tags within attribute values of tags, but what can you do.

Well, I couldn’t have a tag, in a tag, in an attribute of a tag. Apparently you can only take that so far. So, the trick was using the var of the spring:message to store the dateFormat in something that I could later reference as ${dateFormat} in the pattern of the fmt:formatDate.

2010/11/15

Spring MVC binding to Strings and Dates

Filed under: development, spring — Tags: , , , — digitaljoel @ 2:29 pm

I spent the entire morning trying to figure out how to get Spring MVC to allow for Null in my Date field.  I would get an exception if the Date value in the form was left Null.  Once I found that out, I wasn’t getting any validation messages for all the fields marked as @NotNull.  It turns out Spring just set the value to empty string instead of null if the field was empty.  Hibernate’s JSR-303 implementation has a @NotEmpty validation, but I decided to try to keep it to spec.

So, I implemented a custom @InitBinder for my @Controller and had an anonymous implementation of a custom editor all based off an answer on stackoverflow.com.  Finally, I found this bug logged against Roo

https://jira.springsource.org/browse/ROO-190

Using that single line in my @InitBinder method I was then able to set Dates to null.  For the second problem, I used this very helpful blog post by Stefan Reuter

http://blogs.reucon.com/srt/2007/11/25/spring_mvc_null_or_an_empty_string.html

So, now my @InitBinder method looks like this.

    @InitBinder
    public void allowEmptyDateBinding( WebDataBinder binder )
    {
        // Allow for null values in date fields.
        binder.registerCustomEditor( Date.class, new CustomDateEditor( new SimpleDateFormat( getDatePattern()), true ));
        // tell spring to set empty values as null instead of empty string.
        binder.registerCustomEditor( String.class, new StringTrimmerEditor( true ));
    }

And as simple as that I get null instead of empty string for my string values, and I can allow null values in my non-required date fields. Too bad it took me 6 hours this morning to find the answers.

2010/11/01

Accessing Spring Session Beans in JSP

Filed under: development — Tags: , , , — digitaljoel @ 5:23 pm

I’m using Spring MVC in a project.  I wanted to create a session scoped bean that I could reference directly from my JSP.  In this case, it was a bean for holding status or error messages for display in the UI.  It would keep a queue of messages, and would clear them when displayed.

The interface for my MessageUtil class was simple, with an addMessage method for adding a message to the queue, and a getFlashMessages method that gets all messages as a list and clears the internal queue.

The implementation could be equally simple.  Mine has a touch more code in order to pull the actual message text from a resource bundle, but the class definition is very simple

public class MessageUtilImpl implements MessageUtil, Serializable
{
// implementation here
}

In my spring context configuration file, I defined the bean as follows:

    <bean id="messageUtil" class="mypackage.MessageUtilImpl" scope="session">
        <aop:scoped-proxy proxy-target-class="false"/>
        <property name="messageSource" ref="messageSource" />
    </bean>

Where the messageSource contains the bundle for messages. The real ticket here is the aop:scoped-proxy configuration.

Since I wanted to inject this message utility class into my Spring MVC controllers (which are of a greater scope than session scope) it was puking at me. Adding aop:scope-proxy configuration to the bean definition (which apparently isn’t available yet as an annotation, which is why I had to configure in xml instead) allows Spring to use AOP to inject the session bean into the controller for the thread that is processing the request, tying my messageUtil to the one that has been constructed for the current session.

One item of note is the proxy-target-class attribute. If you set it to false, then spring aop will use a java interface based proxy. This means that your bean must have an interface and an implementation, and that everywhere you use the bean, you must reference it via the interface and NOT the implementation. Well… DUH. If you have an interface and an impl, and you are referencing the impl, then what can I say? If you set that value to true, then spring aop will use cglib (which now must be on your build path, probably runtime path) to proxy the implementation class, meaning you don’t need to have an interface and implementation, you can simple have a class. I didn’t want to need cglib, so I chose interface based proxy.

With that magic done, now all I had to do was reference my messageUtil bean in the jsps so I could call the getFlashMessages() method and display them.

Now, I’m no JSP guru. I’ve spent the last 3 years in JSF land. I’m sure I could wire this up to get the messages via ajax and do something super awesome… but I didn’t. I’m using the Pines Notify jQuery plugin to show a simple growl-type message.

<script type="text/javascript" >
    <c:forEach var="flashMessage" items="${sessionScope['scopedTarget.messageUtil'].flashMessages}" >
        $.pnotify( {
            pnotify_title: "${flashMessage.title}",
            pnotify_text: "${flashMessage.message}",
            pnotify_type: "${flashMessage.severity}",
            pnotify_shadow: true
        })
    </c:forEach>
</script>

Besides the aop:scoped-proxy, the thing that took the longest to figure out was how to get the stinking spring session bean. You can see that I’m accessing it like this

${sessionScope['scopedTarget.messageUtil'].flashMessages}

The answer is the ‘scopedTarget’ prefix to the bean name. Since it uses a dot in the name, you can’t use sessionScope.scopedTarget.messageUtil, so the way it’s referenced above is the only way I know how to do it. It took surprisingly long for me to find it.

I’m sure as soon as I publish this, someone will find a reference to it in the official spring documentation, but Adobe Reader didn’t find it in the 799 page Spring Documentation PDF I have.

That’s it for this one. Have fun with spring session scoped beans and jsp el.

2010/10/12

Windows 7 and the Mac Mini Server VPN

Filed under: troubleshooting — Tags: , — digitaljoel @ 12:54 pm

I spent a few hours in the last week and a half attempting to connect my Windows 7 computer to a VPN provided by a new Mac Mini Server.  The win 7 computer is at my house, and the mac mini is at my friend’s house.  The wrinkle here is that we both have routers that provide NAT services.  Little did I know, that would be the only real problem.

In order to configure the connection, you want to set it as an L2TP connection, enable the CHAP and MS-CHAPS authentication scheme, and in the advanced properties for the L2TP connection make sure you put in the plain text shared secret that the VPN server admin should provide to you.

Finally, in order to connect through the NAT devices, you will want to have a look at this link.

http://support.microsoft.com/kb/926179

It doesn’t list Windows 7 in the “Applies to” section, but it worked for me on Windows 7.  The gist is, you want to create this new registry key:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PolicyAgent\AssumeUDPEncapsulationContextOnSendRule.

The path up to PolicyAgent should already exist, so you only have to create the AssumeUDPEncapsulationContextOnSendRule DWORD(32) key.  If your computer AND the VPN server are both behind a NAT device (like a router) then you will want to set the value to 2.  This is likely what you will need.  If only the server is behind a NAT device, then set the value to 1.  If you feel like you need to set the value to 0, then this blog post isn’t for you in the first place.

After changing the registry, you’ll likely have to reboot.

Hopefully this’ll save someone the several hours myself (and my buddy!) had to spend figuring this out.

2010/09/12

JSF graphicImage from database

Filed under: development, facelets, java, JSF, JSF2 — digitaljoel @ 8:45 pm

I’ve seen some questions on the webtier mailing list about loading image resources from a database, but no real implementation suggestions.  I ended up needing to figure this out for work, and got permission to post the solution here.  It’s not 100% complete as it doesn’t allow for caching at the client or in the JSF layer, but it’s a starting point.  You won’t be able to take this solution and simply plop a jar in your project and make it go.  You’ll have to implement your own solution, but can base it on this information if it helps you.

In my case, our software manages Programs and Brands.  What those entities represent isn’t important, but know that each program can have a logo and custom css, and each Brand can have a logo.  I wanted to be able to use the regular JSF tags to output these images so the designers wouldn’t have to learn a new tag.  Something like this:


    <h:outputStylesheet library="program_#{user.programName}" name="css" />
    <h:graphicImage library="program_#{user.programName}" name="logo" />

The first step is to implement a custom ResourceHandler.  This would be super awesome if we could just extend ResourceHandlerWrapper like the JSF developers intended.  They built that class precisely for extension, unfortunately, the implementation has what I consider a fatal flaw.  JSF calls handleResourceRequest in order to create a resource when a resource request comes in.  The resource is a representation of your image or css.  In handleResourceRequest, the ResourceHandlerWrapper calls in to the wrapped instance’s handleResourceRequest, and the ResourceHandlerImpl  handleResource calls its own createResource method.

Update: It looks like they have fixed the ResourceHandlerImpl in 2.0.3 so that it now delegates to the configured resource handler for createResource.  This is great in that it means you should be able to now extend ResourceHandlerWrapper instead of ResourceHandlerImpl and get the correct behavior.  You should view this thread http://forums.java.net/jive/thread.jspa?threadID=153490&tstart=0 for more info.

So, if my extension of ResourceHandlerWrapper implements its own createResource method in order to create my own custom resources, then I must also implement my own handleResourceRequest which, if you look at the linked javadoc would be really easy to mess up all resource handling in your application.

The point of all this rambling?  I had to do something I’m not proud of.  I extended ResourceHandlerImpl, which is a non-published API, but it was that or implement my own handleResourceRequest, which I really didn’t want to do.  So, here’s the source of my ResourceHandler.


package yourpackage.jsf;

import com.sun.faces.application.resource.ResourceHandlerImpl;
import javax.faces.application.Resource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 *
 * @author Joel.Weight
 */
public class DelegatingResourceHandler extends ResourceHandlerImpl
{
    private ResourceHelper[] helpers = new ResourceHelper[] {
    new ProgramResourceHelper()
    , new BrandResourceHelper()
    };

    private static Log log = LogFactory.getLog( DelegatingResourceHandler.class );

    @Override
    public Resource createResource( String resourceName, String libraryName )
    {
        ResourceHelper helper = getHelper( libraryName );
        if ( helper != null )
        {
            return helper.createResource( resourceName, libraryName );
        }
        // otherwise delegate to the default implementation.
        if ( log.isDebugEnabled() )
        {
            log.debug( "Delegating resource creation to default implementation. name: " + resourceName +
            " and library: " + libraryName );
        }
        return super.createResource( resourceName, libraryName, null );
    }

    @Override
    public boolean libraryExists( String libraryName )
    {
        ResourceHelper helper = getHelper( libraryName );
        if ( helper != null )
        {
            return true;
        }
        return super.libraryExists( libraryName );
    }

    /**
    * Get the helper that handles a given library.
    * @param libraryName
    * @return
    */
    private ResourceHelper getHelper( String libraryName )
    {
        for ( ResourceHelper helper : helpers )
        {
            if ( helper.handlesLibrary( libraryName ))
            {
                return helper;
            }
        }
        return null;
    }
}

Alright, what’s going on here.  First, an array of ResourceHelpers.  Since I want to load images from different entities, I have a helper for each one, which will be responsible for instantiating the resource for the given entity.

Next, the createResource method asks each helper if it handles a given library.  Each helper has a prefix that signals to it that it should handle a given resource request.  For instance, program_ and brand_ are the prefixes I have.  If none of the helpers handle the library, then it delegates to the default handler functionality.

Finally libraryExists asks each helper if it handles the given library.  If none of them, then it delegates to the default handler functionality.

Next is the helper implementation.  I created an abstract class so the Program and Brand helpers could share common functionality.  Here it is.


package yourpackage.jsf;

import javax.faces.application.Resource;
import javax.faces.context.FacesContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 *
 * @author Joel.Weight
 */
public abstract class ResourceHelper
{

    protected String prefix;

    private static Log log = LogFactory.getLog( ProgramResourceHelper.class );

    public ResourceHelper( String prefix )
    {
        this.prefix = prefix;
    }

    /**
     * Create a resource with the given name for the given library
     * @param resourceName The resource name, as passed in the JSF tag
     * @param libraryName The library name, as passed in the JSF tag
     * @return The resource, or null if not found.
     */
    public abstract Resource createResource( String resourceName, String libraryName );

    /**
     * remove the prefix from a resourceName
     * @param name resourceName
     * @return the resource name without the prefix, or the original string if it doesn't start with the prefix.
     */
    public String stripPrefix( String name )
    {
        if ( name != null && name.startsWith( prefix ))
        {
            return name.substring( prefix.length() );
        }
        return name;
    }

    /**
     * Test whether this helper handles the given library name.
     * @return true if this helper handles the library, false otherwise.
     */
    public boolean handlesLibrary( String libraryName )
    {
        boolean result = ( libraryName != null
                                && libraryName.toLowerCase().startsWith( prefix )
                                && libraryName.length() > prefix.length());
        if ( log.isDebugEnabled() )
        {
            log.debug( String.format( "Helper with prefix %s handles library %s : %s", prefix, libraryName, result ));
        }
        return result;
    }

    /**
     * Gets a bean from the faces context.
     * @param name name of the bean to get
     * @return bean instance as returned from faces context.
     */
    public Object getBeanFromFacesContext( String name )
    {
        FacesContext context = FacesContext.getCurrentInstance();
        return context.getApplication().getELResolver().getValue( context.getELContext(), null, name );
    }
}

This is pretty simple.  It takes care of a lot of the prefix handling stuff.  The handlesLibrary method is the biggest shared functionality.  It looks at the prefix and determines if the library passed in the graphicImage or outputStylesheet tag should be handled by this helper.

Here is an implementation of the abstract class.  This is the one for Brand.


package yourpackage.jsf;

import awp.data.BrandService;
import awp.view.Brand;
import javax.faces.application.Resource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * ResourceHelper for getting images associated with a Brand.
 * @author Joel.Weight
 */
public class BrandResourceHelper extends ResourceHelper
{

    private BrandService brandService;

    private static Log log = LogFactory.getLog( BrandResourceHelper.class );

    public BrandResourceHelper()
    {
        super( "brand_" );
    }

    @Override
    public Resource createResource( String resourceName, String libraryName )
    {
        Brand brand = getBrandByLibraryName( libraryName );
        if ( brand != null )
        {
            return new BrandResource( resourceName, libraryName, brand );
        }
        return null;
    }

    private Brand getBrandByLibraryName( String libraryName )
    {
        Brand brand = null;
        String brandName = stripPrefix( libraryName );
        if ( brandName != null && brandName.length() > 0 )
        {
            brand = getBrandService().findBrandByName( brandName );
        }
        return brand;
    }

    private BrandService getBrandService()
    {
        if ( brandService == null )
        {
            brandService = (BrandService)getBeanFromFacesContext( "brandService" );
        }
        return brandService;
    }
}

As with the others, it’s quite simple, and the Program implementation looks very similar.  The only thing we really have to implement here is the constructor which sets up the prefix, and the createResource method.  The others are all helper methods.  createResource finds our entity in order to get the image from it.  Your implementation could query for just the image, or do whatever you want here.

Next up is our custom resource implementation.  This one is a little longer because we are extending a fair amount of the functionality in Resource


package yourpackage.jsf;

import yourpackage.DataObject;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.faces.application.Resource;
import javax.faces.application.ResourceHandler;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * base representation of a database backed resource.
 */
public abstract class DataObjectResource<T extends DataObject> extends Resource
{
    protected String name;
    protected String libraryName;
    protected T dataObject;

    private static Log log = LogFactory.getLog( DataObjectResource.class );

    // HTTP Date format required by the HTTP/1.1 RFC
    private static final String LAST_MODIFIED_PATTERN = "EEE, dd MMM yyyy HH:mm:ss zzz";

    public DataObjectResource( String name, String libraryName, T object )
    {
        this.name = name;
        this.libraryName = libraryName;
        this.dataObject = object;
        if ( log.isTraceEnabled() )
        {
            log.trace( "Created new DataObjectResource: libraryName: " + libraryName + " name: " + name );
        }
    }

    /**
    * Get the input stream for this resource.
    * @return
    * @throws IOException
    */
    @Override
    public abstract InputStream getInputStream() throws IOException;

    @Override
    public Map<String, String> getResponseHeaders()
    {
        Map<String, String> result = new HashMap<String, String>( 6, 1.0f );
        SimpleDateFormat format = new SimpleDateFormat( LAST_MODIFIED_PATTERN );
        // make it modified so they always request the whole resource.
        // TODO: make this smarter when going into production.
        result.put( "Last-Modified", format.format( new Date()));
        return result;
    }

    @Override
    public String getRequestPath()
    {
        StringBuilder buf = new StringBuilder(
        FacesContext.getCurrentInstance().getExternalContext().getRequestContextPath() );
        buf.append( ResourceHandler.RESOURCE_IDENTIFIER );
        buf.append( "/" ).append( name ).append( ".faces?ln=" ).append( libraryName );
        if ( log.isDebugEnabled() )
        {
            log.debug( "Request path for program resource " + this.toString() + " : '" + buf.toString() + "'" );
        }
        return buf.toString();
    }

    @Override
    public URL getURL()
    {
        ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
        StringBuilder buf = new StringBuilder( context.getRequestScheme() );
        buf.append( context.getRequestServerName() );
        if ( context.getRequestServerPort() != 80 && context.getRequestServerPort() != 443 )
        {
            buf.append( ":" ).append( context.getRequestServerPort());
        }
        buf.append( getRequestPath());
        URL url = null;
        try
        {
            url = new URL( buf.toString());
            if ( log.isDebugEnabled() )
            {
                log.debug( "Created new URL " + buf.toString() + " for ProgramResource " + this.toString() );
            }
        }
        catch( java.net.MalformedURLException e )
        {
            log.error( "Unable to create URL for ProgramResource " + this.toString(), e);
        }
        return url;
    }

    @Override
    public boolean userAgentNeedsUpdate( FacesContext fc )
    {
        // TODO: always updates the user agent.  fix this to allow for caching.
        return true;
    }

    @Override
    public String toString()
    {
        return String.format( "%s { name=%s libraryName=%s }"
        , this.getClass().getName()
        , name
        , libraryName );
    }
}

This class is pretty dumb, and needs the most help to make it usable in a production system.  It provides default implementations for all of the methods of the Resource, except the getInputStream method.  The good news is that when I get time to come back to this class, it’ll be smarter for every implementation of this abstract class.

Now, to a concrete implementation of our custom Resource class.  This time we’ll look at ProgramResource since it handles css and an image.  I also have an implementation for BrandResource.


package yourpackage.jsf;

import yourpackage.Program;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * JSF Resource implementation used to get information from a Program in the database.
 */
public class ProgramResource extends DataObjectResource<Program>
{
    /**
    * Use this resource name in JSF to get the program logo.
    */
    public static final String LOGO_RESOURCE = "logo";

    /**
    * Use this resource name in JSF to get the program css.
    */
    public static final String CSS_RESOURCE = "css";

    private static Log log = LogFactory.getLog( ProgramResource.class );

    public ProgramResource( String name, String libraryName, Program program )
    {
        super( name, libraryName, program );
    }

    @Override
    public InputStream getInputStream() throws IOException
    {
        if ( LOGO_RESOURCE.equals( this.name ))
        {
            return new ByteArrayInputStream( dataObject.getLogo() );
        }
        else if ( CSS_RESOURCE.equals( this.name ))
        {
            return new ByteArrayInputStream( dataObject.getCss().getBytes());
        }
        else
        {
            if ( log.isWarnEnabled() )
            {
                log.warn( "Attempted to get input stream for ProgramResource, but no correct name specified. " +
                this.toString() );
            }
            return null;
        }
    }
}

First, notice the final static Strings that identify logo and css.  Those are the names of the resources you will use in the graphicImage and outputStylesheet tags.  They tell me where to get the data from for the getInputStream method.  The implementation of getInputStream is quite simple.  We just look at the name of the resource being requested, and use that as a key to figure out which field of our DataObject.

The last part of this puzzle is to tell JSF to use our DelegatingResourceHandler that we implemented at the start.  This is done with the following tag in your faces-config.xml file.  It should be within the application tag.


<resource-handler>yourpackage.jsf.DelegatingResourceHandler</resource-handler>

With all of this in place, I can show a different image in the same place in my application depending on the current context, and pull that image from the database.  For instance, if one user is in Program A, and another is in Program B, I simply use the user’s context to give the program to the graphicImage tag, and the outputStylesheet tag, and I have different styling and images for each of those users, without changing anything in my xhtml.

As I said before, this implementation is far from perfect.  If you attempt to reference a resource library and the helper can’t find it in the database then you will have a problem.  Also if you attempt to reference a resource name that doesn’t tie in to one of your identifiers in your Resource implementation, then you are toast.  Finally, there is no caching currently, so every request will load the image or css from the database.  That’s ugly.  Perhaps when I get back to this code in my current project, I’ll update the source in this blog post.  In the meantime, it ought to be a decent starting point for anyone looking to solve the same problem.

« Newer PostsOlder Posts »

Create a free website or blog at WordPress.com.