Java Ninja Chronicles By Norris Shelton

Things I learned in the pursuit of code

Today, we are going to look at an example of Springframework MVC with form validation via JSR-303 annotations. We will be using the hibernate validations implementation of JSR-303.

The pom.xml will look like. The spring specific dependencies are highlighted.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>norris.shelton</groupId>
    <artifactId>spring3MvcValidation</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>Spring3 MVC Validation</name>

    <properties>
        <project.build.sourceEncoding>Cp1252</project.build.sourceEncoding>
        <spring.version>3.0.5.RELEASE</spring.version>
        <slf4j.version>1.6.1</slf4j.version>
    </properties>

    <dependencies>
        <!--note Servlet, jsp &amp; jstl - almost all provided-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>4.0.2.GA</version>
        </dependency>

        <!--hijack log4j-->
        <!--NOTE Saying it is provided so it will not be packaged in my builds if a dependency is using it-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
            <scope>provided</scope>
        </dependency>

        <!--hijack commons logging-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>0.9.27</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>spring3MvcValidation</finalName>
    </build>
</project>

The web.xml has to have the following to enable the spring dispatcher.

<?xml version="1.0" encoding="UTF-8"?>
<web-app 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-app_2_5.xsd"
         version="2.5">
    <display-name>Spring3 MVC Validation</display-name>
    <description>Spring3 MVC Validation</description>

    <!--
        Spring MVC dispatcher - defaults to /WEB-INF/SERVLETNAME-servlet.xml for the spring mvc context

        Central dispatcher for HTTP request handlers/controllers, e.g. for web UI controllers or HTTP-based remote
        service exporters. Dispatches to registered handlers for processing a web request, providing convenient mapping
        and exception handling facilities.
    -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <!-- /WEB-INF/springmvc-servlet.xml will be the spring mvc context file -->
        <servlet-name>springmvc</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</web-app>

The spring web context is in springmvc-servlet.xml (per the name of the spring mvc dispatcher in the web.xml). Nothing special here. It scans for spring components, says it is annotation driven. There is also a view resolver.

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

    <!--
        Scans the classpath for annotated components that will be auto-registered as Spring beans. By default, the
        Spring-provided @Component, @Repository, @Service, and @Controller stereotypes will be detected.

        This tag implies the effects of the 'annotation-config' tag, activating @Required, @Autowired, @PostConstruct,
        @PreDestroy, @Resource, @PersistenceContext and @PersistenceUnit annotations in the component classes, which is
        usually desired for autodetected components (without external configuration).

        A component scan results in new bean definition being registered; Spring's PropertyPlaceholderConfigurer will
        apply to those bean definitions just like to regular bean definitions, but it won't apply to the component scan
        settings themselves.
    -->
    <context:component-scan base-package="norris.shelton.spring3.mvc.validation"/>

    <!--
        This tag registers the DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdapter beans that are
        required for Spring MVC to dispatch requests to @Controllers.
    -->
    <mvc:annotation-driven/>

    <!--
        Resolves symbolic view names to URLs, without explicit mapping definition. This is useful if your symbolic names
        match the names of your view resources in a straightforward manner (i.e. the symbolic name is the unique part
        of the resource's filename), without the need for a dedicated mapping to be defined for each view.
        TODO: Why doesn't this get created and defaulted with these default values by <mvc:annotation-driven/>
    -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:viewClass="org.springframework.web.servlet.view.JstlView"
          p:prefix="/WEB-INF/jsp/" 
          p:suffix=".jsp"/>
</beans>

The controller is a fairly normal spring mvc annotation driven controller.

package norris.shelton.spring3.mvc.validation;


import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.validation.Valid;


/**
 * Validating Spring MVC controller.
 * @author Norris Shelton
 */
@Controller
public class NameController {

    /**
     * Handles gets of the first name form.
     * @param model spring-provided model object
     * @return the name of the jsp page to resolve
     */
    @RequestMapping(value = "/firstName", method = RequestMethod.GET)
    public String getFirstName(Model model) {
        model.addAttribute("firstNameModel", new FirstNameModel());
        return "firstNameForm";
    }

    /**
     * @param firstNameModel         first name data model
     * @param firstNameBindingResult validator binding result for the first name model
     * @return model and view object
     */
    @RequestMapping(value = "/firstName", method = RequestMethod.POST)
    public String postFirstName(@Valid FirstNameModel firstNameModel, BindingResult firstNameBindingResult) {
        if (firstNameBindingResult.hasErrors()) {
            return "firstNameForm";
        } else {
            return "redirect:/lastName.do";
        }
    }

    /**
     * Handles gets of the last name form.
     * @param model spring-provided model object
     * @return the name of the jsp page to resolve
     */
    @RequestMapping(value = "/lastName", method = RequestMethod.GET)
    public String getLastName(Model model) {
        model.addAttribute("lastNameModel", new LastNameModel());
        return "lastNameForm";
    }

    /**
     * @param lastNameModel         first name data model
     * @param lastNameBindingResult validator binding result for the first name model
     * @return model and view object
     */
    @RequestMapping(value = "/lastName", method = RequestMethod.POST)
    public String postLastName(@Valid LastNameModel lastNameModel, BindingResult lastNameBindingResult) {
        if (lastNameBindingResult.hasErrors()) {
            return "lastNameForm";
        } else {
            return "redirect:/firstName.do";
        }
    }
}

I have two controller methods per form. The first method I use is the getter method. Basically, it says that if the form is requested via a GET, then the form is being requested for the first time. I want to create the model object, then display the view. This is necessary because the spring form tags have to be told the name of the model class. This model has to exist when the form is rendered.

    @RequestMapping(value = "/firstName", method = RequestMethod.GET)
    public String getFirstName(Model model) {
        model.addAttribute("firstNameModel", new FirstNameModel());
        return "firstNameForm";
    }

The second method I use is when the form is posted. This is where the data on the form is sent. This is what triggers the validation. If the binding result object has errors, then go back to the previous form to display the errors, else redirect to the next url. I had to redirect because I had trouble getting it to re-enter the controller.

    @RequestMapping(value = "/firstName", method = RequestMethod.POST)
    public String postFirstName(@Valid FirstNameModel firstNameModel, BindingResult firstNameBindingResult) {
        if (firstNameBindingResult.hasErrors()) {
            return "firstNameForm";
        } else {
            return "redirect:/lastName.do";
        }
    }

@Valid indicates that this model needs validation. Also, there BindingResult needs to be directly after the model validated. This is because if you have multiple models being validated, then you could access multiple binding result objects. In this case, I don’t care what the messages are.

Still very simple and straight forward. Here is what one of the models looks like. The only things out of the ordinary are the annotations on line 11 and 12. I say that the property is required and it must have a length between 2 and 64.

package norris.shelton.spring3.mvc.validation;


import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;


/** @author Norris Shelton */
public class FirstNameModel {

    @NotNull
    @Size(min = 2, max = 64)
    private String firstName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
}

The view for this is also very simple. It uses 3 spring form tags. The label tag generates a HTML label. The input field creates an input form. The “firstName” property is linked to this vield in the “firstNameModel”. The errors tag displays any errors for the firstName property in the firstNameModel. You can also say path=”*” to display all error messages.

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<form:form action="firstName.do" method="post" modelAttribute="firstNameModel">
    <form:label path="firstName">
        First Name:<form:input path="firstName"/> <form:errors path="firstName" cssStyle="color:red;"/>
    </form:label>
    <input type="submit" value="Submit"/>
</form:form>

The entire source code is available here.

February 28th, 2011

Posted In: Spring

2 Comments

Java Enums are starting to become more prevalent. One problem that I found is that if you have some data and need to get the enum for that, then you have to write something custom. I had always done that by:

    public static MyEnum getBySomeValue(String someValue) {
        MyEnum myEnumTarget = null;
        for (MyEnum myEnum : MyEnum.values()) {
            if (myEnum.getSomeValue().equals(someValue)) {
                myEnumTarget = myEnum;
            }
        }
        return myEnumTarget;
    }

I recently ran across a post that showed a better way. I tweaked his example a little to produce:

package com.bhsi.preregistration;


import org.apache.commons.lang.StringUtils;

import java.util.HashMap;
import java.util.Map;


/** @author Norris Shelton */
public enum EmailFormat {
    HTML("H"),
    TEXT("T"),
    NONE(null);

    private static final Map<String, EmailFormat> keys = new HashMap<String, EmailFormat>();
    static {
        for (EmailFormat emailFormat : EmailFormat.values()) {
            keys.put(emailFormat.key(), emailFormat);
        }
    }


    EmailFormat(String key) {
        this.key = key;
    }

    private final String key;

    public String key() {
        return key;
    }

    /**
     * Creates an instance of the email format.
     * @param key the string key of the email format
     * @return an email format type object
     */
    public static EmailFormat keyOf(String key) {
        EmailFormat emailFormat = EmailFormat.NONE;
        if (StringUtils.isNotBlank(key)) {
            emailFormat = keys.get(key);
        }
        return emailFormat;
    }
}

I am favoring this implementation. It consumes a little bit more memory, but is more efficient during the lookup.

Notice line 38. In this case, there were entries in the database. I only cared about “H” and “T”, but the database also had nulls and empty strings. By writing the code the way I did, all of those become NONE and the real values are associated with the HTML and TEXT types.

February 23rd, 2011

Posted In: Java

Leave a Comment

Interfaces are used in Java to define the contract that an implementing class is bound to perform. In it’s essence, that is abstraction. The developer using the interface doesn’t need to be aware of the implementation. Unfortunately, lots of developers make the same mistake with interfaces as they do with design patterns. They see them as something to use for everything. It is my contention, that using an interface is a solution to the problem. If you have the problem, then you use them for abstraction. If you don’t have the problem, then you don’t use them. In this case, the problem that interfaces would be used to solve is the problem of having multiple implementations. If there are multiple implementations, it is helpful for the developer coding to the library to have one interface to code to, that can abstract away the peculiar details that are related to the specific implementations. Then the developer can be given the choice to change the implementation without having to re-code to the new implementation. The developer can code to the interface and that code can support multiple implementations without making a change. That is a very good use of in interface for the purpose of abstraction.

With that being said, I have been said to be anti-interface for a long while. It’s not that I’m against interfaces. I’m more against their abuse. If I have one implementation, then why would I need a interface to form a contract that the implementation must follow. Of course it follows it, it is the very definition. My problem with using an interface when there is only one implementation is that the problem of having to enforce the external interface doesn’t exist when there is only one implementation. There is no need for abstraction. To state it another way, interfaces are intended for developers of an API. If you are coding a solution to your problem, you are rarely if ever coding an API. You are coding a solution. I never worked on a project that received the benefit. Every time I worked on a project that used interfaces, the only benefit I received was double maintenance. Any time I changed the implementation class, it necessitated a change in the interface. I even used them with the idea that we are using JDBC now, but soon-enough, we are going to be using Hibernate. Doh. By the time we moved, the code was gutted so much to be unrecognizable. Thanks a lot.

I can see where they are useful in some cases, just not anything that I am working on.

Adam Bien’s Weblog has a post that contains some nice nuggets of information. When are interfaces a good idea. He states.

  1. Strategy Pattern: there are already several implementations of an algorithm or concept
  2. Layering: there is a clear need to hide e.g. an ugly implementation of a legacy framework
  3. API (not very common): you have to expose a API, which gets implemented by SPI (e.g. JDBC)

Very nice and straight to the point.

February 22nd, 2011

Posted In: Java, java ninja, Javaninja

Leave a Comment

I am working on a Springframework project that accumulates data via a series of web pages. There is basically a table per page and and object per page. This makes it nice and neat 1:1. As I am going through the data, I notice that there is a spouse, an emergency contact, guardian, and a registration that have a ton of fields in common. Basically, they are people. Most of the information that is needed for a person is the same, regardless of the specialized type of person. They all need a first name, last name, address, city, state, zip …. There are eventually 18 fields that all of these objects have in common. I am a big fan of DRY and didn’t want to have a ton of the same properties/getters/setters all over the place.

I ended up with a Person that was extended by a Registration and an AttachedPerson. The AttachedPerson was extended by EmergencyContact, Guardian and Spouse.

Here is what the java code for Spouse looks like:

package com.bhsi.preregistration.spouse;


import com.bhsi.preregistration.AttachedPerson;


/** @author Norris Shelton */
public class Spouse extends AttachedPerson {

    private String middleInitial;

    public String getMiddleInitial() {
        return middleInitial;
    }

    public void setMiddleInitial(String middleInitial) {
        this.middleInitial = middleInitial;
    }
}

Spouse extends AttachedPerson.

package com.bhsi.preregistration;


/**
 * This is the class that supports every person that is attached to a registration (e.g. not the person being
 * pre-registered).
 * @author Norris Shelton
 */
public abstract class AttachedPerson extends Person {
    private int     id;
    private int     registrationId;
    private boolean addressSame;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getRegistrationId() {
        return registrationId;
    }

    public void setRegistrationId(int registrationId) {
        this.registrationId = registrationId;
    }

    public boolean isAddressSame() {
        return addressSame;
    }

    public void setAddressSame(boolean addressSame) {
        this.addressSame = addressSame;
    }
}

AttachedPerson extends Person.

package com.bhsi.preregistration;


import java.sql.Timestamp;


/**
 * This is the base information that every person will have.
 * @author Norris Shelton
 */
public abstract class Person {
    private String    firstName;
    private String    lastName;
    private String    address;
    private String    city;
    private String    state;
    private String    zip;
    private String    homePhone;
    private String    ssn;
    private String    birthDate;
    private String    gender;
    private int       employerStatus;
    private String    employerName;
    private String    employerAddress;
    private String    employerCity;
    private int       employerState;
    private String    employerZip;
    private String    employerPhone;
    private Timestamp employerRetireDate;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public String getZip() {
        return zip;
    }

    public void setZip(String zip) {
        this.zip = zip;
    }

    public String getHomePhone() {
        return homePhone;
    }

    public void setHomePhone(String homePhone) {
        this.homePhone = homePhone;
    }

    public String getSsn() {
        return ssn;
    }

    public void setSsn(String ssn) {
        this.ssn = ssn;
    }

    public String getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(String birthDate) {
        this.birthDate = birthDate;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public int getEmployerStatus() {
        return employerStatus;
    }

    public void setEmployerStatus(int employerStatus) {
        this.employerStatus = employerStatus;
    }

    public String getEmployerName() {
        return employerName;
    }

    public void setEmployerName(String employerName) {
        this.employerName = employerName;
    }

    public String getEmployerAddress() {
        return employerAddress;
    }

    public void setEmployerAddress(String employerAddress) {
        this.employerAddress = employerAddress;
    }

    public String getEmployerCity() {
        return employerCity;
    }

    public void setEmployerCity(String employerCity) {
        this.employerCity = employerCity;
    }

    public int getEmployerState() {
        return employerState;
    }

    public void setEmployerState(int employerState) {
        this.employerState = employerState;
    }

    public String getEmployerZip() {
        return employerZip;
    }

    public void setEmployerZip(String employerZip) {
        this.employerZip = employerZip;
    }

    public String getEmployerPhone() {
        return employerPhone;
    }

    public void setEmployerPhone(String employerPhone) {
        this.employerPhone = employerPhone;
    }

    public Timestamp getEmployerRetireDate() {
        return employerRetireDate;
    }

    public void setEmployerRetireDate(Timestamp employerRetireDate) {
        this.employerRetireDate = employerRetireDate;
    }
}

This worked great until I started to write the Spring JDBC RowMapper. I was faced with the prospect of having the same properties being set amongst several RowMapper objects. I Googled “inheritance spring rowmapper” and didn’t find anything relevant. I tinkered with this and come up with the following.

We will use Spouse RowMapper as the example.

package com.bhsi.preregistration.spouse;


import com.bhsi.preregistration.AttachedPersonRowMapper;
import org.springframework.jdbc.core.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;


/** @author Norris Shelton */
public class SpouseRowMapper implements RowMapper<Spouse> {
    /**
     * Implementations must implement this method to map each row of data in the ResultSet. This method should not call
     * <code>next()</code> on the ResultSet; it is only supposed to map values of the current row.
     * @param rs     the ResultSet to map (pre-initialized for the current row)
     * @param rowNum the number of the current row
     * @return the result object for the current row
     * @throws SQLException if a SQLException is encountered getting column values (that is, there's no need to catch
     *                      SQLException)
     */
    public Spouse mapRow(ResultSet rs, int rowNum) throws SQLException {
        Spouse spouse = new Spouse();
        spouse.setMiddleInitial(rs.getString("spouse_middle_int"));

        return (Spouse) AttachedPersonRowMapper.mapRow(spouse, rs);
    }
}

Everything is typical Spring JDBC RowMapper until line 24. It then makes a call to the attached person row mapper.

package com.bhsi.preregistration;


import java.sql.ResultSet;
import java.sql.SQLException;


/** @author Norris Shelton */
public class AttachedPersonRowMapper {
    /**
     * Implementations must implement this method to map each row of data in the ResultSet. This method should not
     * call <code>next()</code> on the ResultSet; it is only supposed to map values of the current row.
     * @param attachedPerson instantiated AttachedPerson object or sub-class.
     * @param rs             the ResultSet to map (pre-initialized for the current row)
     * @return the result object for the current row
     * @throws SQLException if a SQLException is encountered getting column values (that is, there's no need to catch
     *                      SQLException)
     */
    public static AttachedPerson mapRow(AttachedPerson attachedPerson, ResultSet rs) throws SQLException {
        attachedPerson.setId(rs.getInt("id"));
        attachedPerson.setRegistrationId(rs.getInt("reg_id"));

        String columnPrefix = PersonRowMapper.getColumnPrefix(attachedPerson);

        attachedPerson.setAddressSame(rs.getBoolean(columnPrefix + "address_same"));

        return (AttachedPerson) PersonRowMapper.mapRow(attachedPerson, rs);
    }
}

I wanted to stay as close to the Spring RowMapper as possible for ease of maintenance by other developers. I tried to use Generics, but ran into Java’s Generics implementation limitation. I settled on passing in the concrete object and having the method use the object type needed for that RowMapper to perform it’s task. Then when it comes back, I have to cast it back to the concrete type. This is not as clean as i wanted it to be, but it worked and didn’t make me feel dirty and used.

Again, everything is normal until line 27. This makes a call to the person RowMapper.

package com.bhsi.preregistration;


import com.bhsi.preregistration.emergencycontact.EmergencyContact;
import com.bhsi.preregistration.guardian.Guardian;
import com.bhsi.preregistration.spouse.Spouse;

import java.sql.ResultSet;
import java.sql.SQLException;


/** @author Norris Shelton */
public class PersonRowMapper {
    /**
     * Implementations must implement this method to map each row of data in the ResultSet. This method should not
     * call <code>next()</code> on the ResultSet; it is only supposed to map values of the current row.
     * @param person       instantiated AttachedPerson object or sub-class.
     * @param rs           the ResultSet to map (pre-initialized for the current row)
     * @return the result object for the current row
     * @throws SQLException if a SQLException is encountered getting column values (that is, there's no need to catch
     *                      SQLException)
     */
    public static Person mapRow(Person person, ResultSet rs) throws SQLException {
        String columnPrefix = getColumnPrefix(person);
        person.setFirstName(rs.getString(columnPrefix + "first_name"));
        person.setLastName(rs.getString(columnPrefix + "last_name"));
        person.setAddress(rs.getString(columnPrefix + "address"));
        person.setCity(rs.getString(columnPrefix + "city"));
        person.setState(rs.getString(columnPrefix + "state"));
        person.setZip(rs.getString(columnPrefix + "zip"));
        person.setHomePhone(rs.getString(columnPrefix + "home_phone"));
        person.setSsn(rs.getString(columnPrefix + "ssn"));
        person.setBirthDate(rs.getString(columnPrefix + "birthdate"));
        person.setGender(rs.getString(columnPrefix + "gender"));
        person.setEmployerStatus(rs.getInt(columnPrefix + "emp_status"));
        person.setEmployerName(rs.getString(columnPrefix + "employer"));
        person.setEmployerAddress(rs.getString(columnPrefix + "employer_address"));
        person.setEmployerCity(rs.getString(columnPrefix + "employer_city"));
        person.setEmployerState(rs.getInt(columnPrefix + "employer_state"));
        person.setEmployerZip(rs.getString(columnPrefix + "employer_zip"));
        person.setEmployerPhone(rs.getString(columnPrefix + "employer_phone"));
        person.setEmployerRetireDate(rs.getTimestamp(columnPrefix + "employer_retiredate"));
        return person;
    }

    /**
     * Gets the appropriate column prefix based upon the true type of the object
     * @param person person sub-type
     * @return column prefix to add to the beginning of certain database columns
     */
    public static String getColumnPrefix(Person person) {
        String columnPrefix = "";
        if (person instanceof Guardian) {
            columnPrefix = "guar_";
        } else if (person instanceof Spouse) {
            columnPrefix = "spouse_";
        } else if (person instanceof EmergencyContact) {
            columnPrefix = "em_";
        }
        return columnPrefix;
    }

}

Just to make my life more interesting, I see that the tables have the same columns, BUT someone has decided to add a prefix to the beginning of the fields. Instead of first_name in all of the tables, spous_ was added to the beginnings of the fields in the spouse table. Luckily they consistently added the prefix to the same columns in all of the tables. I had to fall back to instanceof to get the concrete type to determine what prefix, if any to use.

There you have it. This is how I used inheritance in combination with Spring JDBC RowMapping.

February 16th, 2011

Posted In: Java, Spring

One Comment

I have seen 3 different approaches to making a Vignette Portal templating webapp to hold your custom code.

  1. Make your files in an area that mirrors a webapp
    This is deployed by copying the vgn-ext-templating webapp to your web server deploy directory, then copying your files into the exploded directory
    Pros
    This keeps your files separate from the vignette code
    Makes it easy to upgrade the base vignette webapp
    Cons
    You are not really in a webapp so you probably don’t have access to their libraries
    May not have good syntax highlighting from your IDE without jumping through hoops
    Verdict
    This is useful for only the smallest of customizations and your are probably editing your jsp files by hand

  2. Add your files to a copy of the vgn-ext-templating webapp
    Pros
    This gives your access to the vignette provided libraries
    Your code is in a structure that is a valid webapp
    Cons
    Your code is mixed in with the vignette provided code
    Verdict
    This is much better for IDE support, but future vignette upgrades have to be done by hand

  3. Create a maven project that inherits the vgn-ext-templating webpp
    Pros
    This gives your access to the vignette provided libraries
    Your code is in a structure that is a valid webapp
    Maven can be used to manage the dependencies
    Your code is kept separate from the vignette provided code
    Cons
    You will need to verify that newer versions of the libraries are compatible with the previously provided vignette libraries
    Verdict
    This is much better for IDE support and future vignette upgrades can be accomplished quickly

  4. I saw approach one used on a vignette portal 7.6 webapp. This was for a very small webapp with only jsp files. No real code. The custom code is composed of jsp files that function as vignette jsp overrides for list and detail pages.

    Approach two was used on an intranet webapp on vignette portal 7.6 sp1 webapp. There was considerable custom code. To facilitate this, the project was converted to a maven project. The vignette libraries were made available via maven system scope dependencies, served out of the WEB-INF/lib directory. Any 3rd party libs that could be identified, they were replaced with normal maven dependencies. This worked out well until vignette portal 7.6 sp2_patch came along. The upgrade forced a reevaluation of the way we built the webapp.

    This reevaluation led to approach 3. To test this approach, I took the webapp that had been used to build approach one and put just the custom code in a maven webapp. Then I built a parent pom that included the vignette vgn-ext-templating webapp. At this point, it used vignette portal 7.6 sp1 libraries. I then made a parent pom that had the vignette portal 7.6 sp2_patch libraries. It only took 30 minutes to diff the two libraries to find the differences.

    At this point, it seems that approach 3 is by far the best approach.

    Here is how you do it:
    The vignette provided libraries were added to the local repository. Basic spring libraries were added along with logback for logging. Any known libraries were imported as a maven central repo dependency. This helps in cases where subsequent dependencies need a newer version of the library. Maven will ensure that the newer version is built into the artifact, but not the older version. This carries a risk of a newer library not being compatible with the version that is needed by vignette. This has not materialized at this point, but must be kept in mind.

    This is what the parent pom.xml looks like.

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.bhsi</groupId>
        <artifactId>vgn-ext-templating</artifactId>
        <version>7.6sp2_20110101_patch</version>
        <name>BHSI Maven-Vignette Ext Templating Parent</name>
        <packaging>pom</packaging>
    
        <developers>
            <developer>
                <name>Norris Shelton</name>
            </developer>
        </developers>
    
        <distributionManagement>
            <repository>
                <id>nexus</id>
                <name>nexus</name>
                <url>http://lxbhi320:8091/nexus/content/repositories/releases</url>
            </repository>
        </distributionManagement>
    
        <properties>
            <vignette.version>7.6sp2_20110101_patch</vignette.version>
            <spring.version>3.0.5.RELEASE</spring.version>
            <slf4j.version>1.6.1</slf4j.version>
        </properties>
    
        <dependencies>
            <!-- the overlay is part of what makes this project a Vignette template-->
            <dependency>
                <groupId>com.bhsi</groupId>
                <artifactId>vgn-ext-templating-web</artifactId>
                <version>${vignette.version}</version>
                <type>war</type>
                <scope>runtime</scope>
            </dependency>
    
            <!--note from the original port of vgn-ext-templating to maven -->
            <dependency>
                <groupId>javax.activation</groupId>
                <artifactId>activation</artifactId>
                <version>1.0.2</version>
            </dependency>
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=autonomyclient -Dfile=autonomyClient.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>autonomyClient</artifactId>
                <version>${vignette.version}</version>
            </dependency>
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=axis -Dfile=axis.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>axis</artifactId>
                <version>${vignette.version}</version>
            </dependency>
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=castor -Dfile=castor.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>castor</artifactId>
                <version>${vignette.version}</version>
            </dependency>
            <dependency>
                <groupId>commons-collections</groupId>
                <artifactId>commons-collections</artifactId>
                <version>2.1</version>
            </dependency>
            <!--note commons dbcp for jdk 5 &amp; below -->
            <dependency>
                <groupId>commons-dbcp</groupId>
                <artifactId>commons-dbcp</artifactId>
                <version>1.3</version>
            </dependency>
            <dependency>
                <groupId>commons-discovery</groupId>
                <artifactId>commons-discovery</artifactId>
                <version>0.2</version>
            </dependency>
            <dependency>
                <groupId>commons-fileupload</groupId>
                <artifactId>commons-fileupload</artifactId>
                <version>1.1</version>
            </dependency>
            <!--note they are using the OLD api.  Need to investigate upgrading to 4-->
            <dependency>
                <groupId>commons-httpclient</groupId>
                <artifactId>commons-httpclient</artifactId>
                <version>3.0-rc2</version>
            </dependency>
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>1.1</version>
            </dependency>
            <!-- note marked as provided so it will not be packaged-->
            <dependency>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
                <version>1.1.1</version>
                <scope>provided</scope>
            </dependency>
            <!--note hijack common logging-->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>jcl-over-slf4j</artifactId>
                <version>${slf4j.version}</version>
            </dependency>
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=hibernate -Dfile=hibernate.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>hibernate</artifactId>
                <version>${vignette.version}</version>
            </dependency>
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=hsqldb -Dfile=hsqldb.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>hsqldb</artifactId>
                <version>${vignette.version}</version>
            </dependency>
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=jaxb-api -Dfile=jaxb-api.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>jaxb-api</artifactId>
                <version>${vignette.version}</version>
            </dependency>
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=jaxb-impl -Dfile=jaxb-impl.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>jaxb-impl</artifactId>
                <version>${vignette.version}</version>
            </dependency>
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=jaxrpc -Dfile=jaxrpc.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>jaxrpc</artifactId>
                <version>${vignette.version}</version>
            </dependency>
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=jsafe -Dfile=jsafe.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>jsafe</artifactId>
                <version>${vignette.version}</version>
            </dependency>
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=jsr173_1.0_api -Dfile=jsr173_1.0_api.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>jsr173_1.0_api</artifactId>
                <version>${vignette.version}</version>
            </dependency>
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=log4j -Dfile=log4j.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>log4j</artifactId>
                <version>${vignette.version}</version>
            </dependency>
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=pullparser -Dfile=pullparser.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>pullparser</artifactId>
                <version>${vignette.version}</version>
            </dependency>
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=saaj -Dfile=saaj.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>saaj</artifactId>
                <version>${vignette.version}</version>
            </dependency>
    
            <!--note vignette-provided libraries-->
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=taglibs-i18n -Dfile=taglibs-i18n.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>taglibs-i18n</artifactId>
                <version>${vignette.version}</version>
            </dependency>
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=vgn-appsvcs-cda -Dfile=vgn-appsvcs-cda.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>vgn-appsvcs-cda</artifactId>
                <version>${vignette.version}</version>
            </dependency>
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=vgnhpd-7.1 -Dfile=vgnhpd-7.1.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>vgnhpd-7.1</artifactId>
                <version>${vignette.version}</version>
            </dependency>
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=vgnhpdwrapper -Dfile=vgnhpdwrapper.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>vgnhpdwrapper</artifactId>
                <version>${vignette.version}</version>
            </dependency>
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=vgn-portal-combined -Dfile=vgn-portal-combined.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>vgn-portal-combined</artifactId>
                <version>${vignette.version}</version>
            </dependency>
    
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=wsdl4j -Dfile=wsdl4j.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>wsdl4j</artifactId>
                <version>${vignette.version}</version>
            </dependency>
            <dependency>
                <groupId>xalan</groupId>
                <artifactId>xalan</artifactId>
                <version>2.6.0</version>
            </dependency>
            <!--mvn install:install-file -DgroupId=com.vignette.vgn-ext-templating -Dversion=${vignette.version} -Dpackaging=jar -DgeneratePom=true -DartifactId=xerces -Dfile=xerces.jar-->
            <dependency>
                <groupId>com.vignette.vgn-ext-templating</groupId>
                <artifactId>xerces</artifactId>
                <version>${vignette.version}</version>
            </dependency>
    
            <!--note Servlet, jsp &amp; jstl - all provided-->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>servlet-api</artifactId>
                <version>2.5</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>javax.servlet.jsp</groupId>
                <artifactId>jsp-api</artifactId>
                <version>2.1</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>jstl</artifactId>
                <version>1.2</version>
            </dependency>
    
            <!--note Spring framework-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${spring.version}</version>
            </dependency>
    
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.8.2</version>
                <scope>test</scope>
            </dependency>
    
            <!-- LogBack -->
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>0.9.24</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>${slf4j.version}</version>
            </dependency>
        </dependencies>
    
        <build>
            <finalName>vgn-ext-templating-dependencies</finalName>
        </build>
    </project>
    

    Notice the very first dependiency.

            <!-- the overlay is part of what makes this project a Vignette template-->
            <dependency>
                <groupId>com.bhsi</groupId>
                <artifactId>vgn-ext-templating-web</artifactId>
                <version>${vignette.version}</version>
                <type>war</type>
                <scope>runtime</scope>
            </dependency>
    

    Maven has the ability to include a war as a dependency. It becomes an overlay and both of the webapps work together. If your module produces a war, then your web.xml will be chosen. The libs had to be included in the parent pom as dependencies because even though maven will include the war that does include libs, those libs are not made available to your classpath. All of the libs were removed from the webapp and included via the parent. Ugh. Hopefully they will change this in a future version and you can get this done with only one maven project.

    This is what the pom.xml for the vgn-ext-templating overlay webapp looks like.

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.bhsi</groupId>
        <artifactId>vgn-ext-templating-web</artifactId>
        <version>7.6sp2_20110101_patch</version>
        <packaging>war</packaging>
        <name>BHSI Maven-Vignette Ext Templating webapp</name>
    
        <developers>
            <developer>
                <name>Norris Shelton</name>
            </developer>
        </developers>
    
        <distributionManagement>
            <repository>
                <id>nexus</id>
                <name>nexus</name>
                <url>http://lxbhi320:8091/nexus/content/repositories/releases</url>
            </repository>
        </distributionManagement>
    
        <build>
            <finalName>vgn-ext-templating-web</finalName>
        </build>
    </project>
    

    This is the script that I wrote to deploy the vignette dependencies into the local maven repository.

    REM These are the commands to import the vgn-ext-templating dependency jars (WEB-INF/lib) into your local maven repository
    REM You will then need to use the Sonatype GUI to "deploy" them to that repository for use by other people.
    
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=autonomyclient     -Dfile=autonomyClient.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=axis               -Dfile=axis.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=castor             -Dfile=castor.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=hibernate          -Dfile=hibernate.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=hsqldb             -Dfile=hsqldb.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=jaxb-api           -Dfile=jaxb-api.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=jaxb-impl          -Dfile=jaxb-impl.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=jaxrpc             -Dfile=jaxrpc.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=jsafe              -Dfile=jsafe.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=jsr173_1.0_api     -Dfile=jsr173_1.0_api.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=log4j              -Dfile=log4j.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=pullparser         -Dfile=pullparser.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=saaj               -Dfile=saaj.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=taglibs-i18n       -Dfile=taglibs-i18n.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=vgn-appsvcs-cda    -Dfile=vgn-appsvcs-cda.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=vgnhpd-7.1         -Dfile=vgnhpd-7.1.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=vgnhpdwrapper      -Dfile=vgnhpdwrapper.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=vgn-portal-combined -Dfile=vgn-portal-combined.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=wsdl4j             -Dfile=wsdl4j.jar
    call mvn deploy:deploy-file -DrepositoryId=nexus -DrepositoryLayout=default -Durl=http://lxbhi320:8091/nexus/content/repositories/releases -DgroupId=com.vignette.vgn-ext-templating -Dversion=7.6sp2_20110101_patch -Dpackaging=jar -DgeneratePom=true -DartifactId=xerces             -Dfile=xerces.jar
    

    Once this is done, you can convert any webapp into a vignette portal vgn-ext-templating webapp by setting the parent of a project to be the parent pom that was created by adding the following to your pom.xml

        <parent>
            <!-- note this is what makes this project a Vignette template-->
            <groupId>com.bhsi</groupId>
            <artifactId>vgn-ext-templating</artifactId>
            <version>7.6sp2_20110101_patch</version>
        </parent>
    

    February 13th, 2011

    Posted In: Java, Maven, Spring, vignette

    Leave a Comment

This is an example of setting up a Springframework project with Maven for dependency management and building with Junit for unit tests. In your pom.xml, you will need the artifacts for Spring and JUnit. This file is located in your root directory. You can get this location via the ${basedir} maven expression.

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>norris.shelton</groupId>
    <artifactId>spring3txJunit4</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>Spring3 Transactions JUnit4</name>


    <properties>
        <spring.version>3.0.5.RELEASE</spring.version>
        <slf4j.version>1.6.1</slf4j.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>jsr250-api</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2</version>
        </dependency>

        <!-- 1.3 is the version for JDK 5, 1.4 is for JDK 6 -->
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.3</version>
        </dependency>
        <!--MS SQL Server driver-->
        <dependency>
            <groupId>net.sourceforge.jtds</groupId>
            <artifactId>jtds</artifactId>
            <version>1.2.5</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.8.2</version>
            <scope>test</scope>
        </dependency>


        <!-- LogBack -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>0.9.27</version>
        </dependency>

        <!--hijack log4j-->
        <!--NOTE Saying it is provided so it will not be packaged in my builds if a dependency is using it-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
            <scope>provided</scope>
        </dependency>

        <!--hijack commons logging-->
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>spring3txJunit4</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.7.1</version>
                <configuration>
                    <!--<parallel>methods</parallel>-->
                    <!--<parallel>classes</parallel>-->
                    <!--<parallel>both</parallel>-->
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Create a class that can be injected by spring. The @Repository annotation is what identifies this as an injectible class. @Repository is a specialization of @Component stereotype. If you annotate your class with @Repository, then spring will automatically translate exceptions in your persistence layer. This java source files are in ${basedir}/src/main/java/norris/shelton/spring3junit4.

package norris.shelton.spring3junit4;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;


/**
 * Data access object for the application data.  This is a spring @Repository class and will receive automatic exception
 * translation.
 * @author Norris Shelton
 * @see Repository
 * @see JdbcTemplate
 * @see NamedParameterJdbcTemplate
 */
@Repository
public class ApplicationDao {
    private static Logger log = LoggerFactory.getLogger(ApplicationDao.class);
    /** These are just the fields that would be inserted (e.g. no id). */
    private static final String SQL_INSERT_FIELDS =
        " name, description, entity_ids, acronym, aka1, aka2, aka3, status, helpGroup, version, date_updated, vendor, approved, display ";

    /** these are the fields that would be selected from. */
    private static final String SQL_FIELDS =
    " id, " + SQL_INSERT_FIELDS;

    /** SQL select statement. */
    private static final String SQL_SELECT_FROM =
        " SELECT " + SQL_FIELDS + " FROM dirApp ";

    /** SQL order by clause. */
    private static final String SQL_ORDER_BY = " ORDER BY status DESC, name ";

    /**
     * Inject the spring object with this name.  This is a jsr-250 annotation.  You can accomplish nearly the same thing
     * with @Autowired to find the objects of the correct type along with @Qualifier("name") to indicate which specific
     * bean you want.
     */
    @Resource(name = "bhsAuxCmsJdbcTemplate")
    private JdbcTemplate jdbcTemplate;

    /**
     * Inject the spring object with this name.  This is a jsr-250 annotation.  You can accomplish nearly the same thing
     * with @Autowired to find the objects of the correct type along with @Qualifier("name") to indicate which specific
     * bean you want.
     */
    @Resource(name = "bhsAuxCmsNamedParameterJdbcTemplate")
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    /**
     * Get an application by id.
     * @param id the application
     * @return the desired application
     * @see JdbcTemplate#queryForObject(String, Object[], RowMapper)   
     */
    public Application get(int id) {
        // use spring jdbc template to query database using a prepared statement and a user provided row mapper to
        // return a domain object
        return jdbcTemplate.queryForObject(SQL_SELECT_FROM + " WHERE id = ? " + SQL_ORDER_BY,
                                  new Integer[]{id},
                                  new ApplicationRowMapper());
    }

    /**
     * Get all applications.
     * @return list of application objects
     * @see JdbcTemplate#query(String, RowMapper) 
     */
    public List<Application> getAll() {
        // use spring jdbc template to query database using a user provided row mapper to return a list of domain
        // objects
        return jdbcTemplate.query(SQL_SELECT_FROM + SQL_ORDER_BY,
                                  new ApplicationRowMapper());
    }


    /**
     * Get all active applications.
     * @return list of active application objects
     * @see JdbcTemplate#query(String, RowMapper)
     */
    public List<Application> getDisplayable() {
        // use spring jdbc template to query database using a user provided row mapper to return a list of domain
        // objects
        return jdbcTemplate.query(SQL_SELECT_FROM + " WHERE display = 1 " + SQL_ORDER_BY,
                                  new ApplicationRowMapper());
    }

    /**
     * Get all active applications.
     * @return list of active application objects
     * @see JdbcTemplate#query(String, RowMapper)
     */
    public List<Application> getActive() {
        // use spring jdbc template to query database using a user provided row mapper to return a list of domain
        // objects
        return jdbcTemplate.query(SQL_SELECT_FROM + " WHERE status = 1 " + SQL_ORDER_BY,
                                  new ApplicationRowMapper());
    }

    /**
     * Creates a new application record.
     * @param application object to be inserted
     * @return the number of rows affected
     * @see NamedParameterJdbcTemplate#update(String, Map)
     * @see BeanPropertySqlParameterSource
     */
    public int insert(Application application) {
        // use spring named parameter jdbc template to insert data into the database using a bean property sql parameter
        // source to retrieve the property values from the domain object by bean property name
        return namedParameterJdbcTemplate.update("INSERT INTO dirApp (" + SQL_INSERT_FIELDS + ") " +
                                                 " values(:name, " +
                                                 "        :description, " +
                                                 "        :entityIds, " +
                                                 "        :acronym, " +
                                                 "        :aka1, " +
                                                 "        :aka2, " +
                                                 "        :aka3, " +
                                                 "        :status, " +
                                                 "        :helpGroup, " +
                                                 "        :version, " +
                                                 "        GETDATE(), " +
                                                 "        :vendor, " +
                                                 "        :approved, " +
                                                 "        :display) ",
                                                 new BeanPropertySqlParameterSource(application));
    }


    /**
     * Updates the given application object.
     * @param application object to be updated
     * @return the number of rows affected
     * @see NamedParameterJdbcTemplate#update(String, Map)
     * @see BeanPropertySqlParameterSource
     */
    public int update(Application application) {
        // use spring named parameter jdbc template to update the database using a bean property sql parameter source to
        // retrieve the property values from the domain object by bean property name
        return namedParameterJdbcTemplate.update("UPDATE dirApp SET name = :name, " +
                                                 "    description = :description, " +
                                                 "    entity_ids = :entityIds, " +
                                                 "    acronym = :acronym, " +
                                                 "    aka1 = :aka1, " +
                                                 "    aka2 = :aka2, " +
                                                 "    aka3 = :aka3, " +
                                                 "    status = :status, " +
                                                 "    helpGroup = :helpGroup, " +
                                                 "    version = :version, " +
                                                 "    date_updated = GETDATE(), " +
                                                 "    vendor = :vendor, " +
                                                 "    display = :display, " +
                                                 "    approved = :approved " +
                                                 " WHERE id = :id ",
                                                 new BeanPropertySqlParameterSource(application));
    }
}

Note, there are other spring APIs being used which require other classes. The missing functionality is needed by spring jdbc. Here is the model bean class.

package norris.shelton.spring3junit4;


import org.apache.commons.lang.builder.ReflectionToStringBuilder;

import java.util.Date;


/** @author Norris Shelton */
public class Application implements Comparable<Application> {
    public static final String SESSION_KEY = "norris.shelton.directories.applications.Applicatioin";
    
    private int id;
    private String name;
    private String description;
    private String entityIds;
    private String acronym;
    private String aka1;
    private String aka2;
    private String aka3;
    private boolean status;
    private String helpGroup;
    private String version;
    private Date dateUpdated;
    private String vendor;
    private boolean display = true;
    private boolean approved;

    public Application() { }

    public Application(Application application) {
        id = application.getId();
        name = application.getName();
        description = application.getDescription();
        entityIds = application.getEntityIds();
        acronym = application.getAcronym();
        aka1 = application.getAka1();
        aka2 = application.getAka2();
        aka3 = application.getAka3();
        status = application.isStatus();
        helpGroup = application.getHelpGroup();
        version = application.getVersion();
        dateUpdated = application.getDateUpdated();
        vendor = application.getVendor();
        display = application.isDisplay();
        approved = application.isApproved();
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getEntityIds() {
        return entityIds;
    }

    public void setEntityIds(String entityIds) {
        this.entityIds = entityIds;
    }

    public String getAcronym() {
        return acronym;
    }

    public void setAcronym(String acronym) {
        this.acronym = acronym;
    }

    public String getAka1() {
        return aka1;
    }

    public void setAka1(String aka1) {
        this.aka1 = aka1;
    }

    public String getAka2() {
        return aka2;
    }

    public void setAka2(String aka2) {
        this.aka2 = aka2;
    }

    public String getAka3() {
        return aka3;
    }

    public void setAka3(String aka3) {
        this.aka3 = aka3;
    }

    public boolean isStatus() {
        return status;
    }

    public void setStatus(boolean status) {
        this.status = status;
    }

    public String getHelpGroup() {
        return helpGroup;
    }

    public void setHelpGroup(String helpGroup) {
        this.helpGroup = helpGroup;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public Date getDateUpdated() {
        return dateUpdated;
    }

    public void setDateUpdated(Date dateUpdated) {
        this.dateUpdated = dateUpdated;
    }

    public String getVendor() {
        return vendor;
    }

    public void setVendor(String vendor) {
        this.vendor = vendor;
    }

    public boolean isDisplay() {
        return display;
    }

    public void setDisplay(boolean display) {
        this.display = display;
    }

    public boolean isApproved() {
        return approved;
    }

    public void setApproved(boolean approved) {
        this.approved = approved;
    }


    @Override
    public boolean equals(Object o) {
        if (this == o) { return true; }
        if (o == null || getClass() != o.getClass()) { return false; }

        Application that = (Application) o;

        if (name != null ? !name.equals(that.name) : that.name != null) { return false; }

        return true;
    }

    @Override
    public int hashCode() {
        return name != null ? name.hashCode() : 0;
    }

    /**
     * Compares this object with the specified object for order.  Returns a negative integer, zero, or a positive
     * integer as this object is less than, equal to, or greater than the specified object.<p>
     * @param o the Object to be compared.
     * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than
     *         the specified object.
     * @throws ClassCastException if the specified object's type prevents it from being compared to this Object.
     */
    public int compareTo(Application o) {
        return name.compareTo(o.getName());
    }

    /**
     * Returns a string representation of the object. In general, the <code>toString</code> method returns a string that
     * "textually represents" this object. The result should be a concise but informative representation that is easy
     * for a person to read. It is recommended that all subclasses override this method.
     * @return a string representation of the object.
     */
    @Override
    public String toString() {
        return ReflectionToStringBuilder.toString(this);
    }
}

This is the row mapper to map the database result set to an object.

package norris.shelton.spring3junit4;


import org.springframework.jdbc.core.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;


/**
 * Application row mapper
 * @author Norris Shelton
 */
public class ApplicationRowMapper implements RowMapper<Application> {
    /**
     * Implementations must implement this method to map each row of data in the ResultSet. This method should not call
     * <code>next()</code> on the ResultSet; it is only supposed to map values of the current row.
     * @param rs     the ResultSet to map (pre-initialized for the current row)
     * @param rowNum the number of the current row
     * @return the result object for the current row
     * @throws SQLException if a SQLException is encountered getting column values (that is, there's no need to catch
     *                      SQLException)
     */
    public Application mapRow(ResultSet rs, int rowNum)
    throws SQLException {
        Application application = new Application();
        application.setId(rs.getInt("id"));
        application.setName(rs.getString("name"));
        application.setDescription(rs.getString("description"));
        application.setEntityIds(rs.getString("entity_ids"));
        application.setAcronym(rs.getString("acronym"));
        application.setAka1(rs.getString("aka1"));
        application.setAka2(rs.getString("aka2"));
        application.setAka3(rs.getString("aka3"));
        application.setStatus(rs.getBoolean("status"));
        application.setHelpGroup(rs.getString("helpGroup"));
        application.setVersion(rs.getString("version"));
        application.setDateUpdated(rs.getDate("date_updated"));
        application.setVendor(rs.getString("vendor"));
        application.setDisplay(rs.getBoolean("display"));
        application.setApproved(rs.getBoolean("approved"));
        return application;
    }
}

Now that we have a spring bean, let’s test it. This is the junit unit test class. This java test source files are in ${basedir}/src/test/java/norris/shelton/spring3junit4

package norris.shelton.spring3junit4;


import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

import static org.junit.Assert.*;


/**
 * This class uses the default for the context configuration and the transaction manager.  It is set-up to work as a
 * unit test instead of an integration test.  It only has what it needs.  However, this would probably lead to some
 * duplication if common parts are not imported by the context file or specified in the context configuration.
 * @author Norris Shelton
 */
@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({TransactionalTestExecutionListener.class,
                         DependencyInjectionTestExecutionListener.class})
@ContextConfiguration
@Transactional
public class TestApplicationDao {

    @Autowired
    private ApplicationDao applicationDao;

    @Test
    public void testGet() {
        Application application = applicationDao.get(1);
        commonAssertions(application);
    }

    @Test
    public void testGetAll() {
        List<Application> applications = applicationDao.getAll();
        assertNotNull(applications);
        for (Application application : applications) {
            commonAssertions(application);
        }
    }

    @Test
    public void testGetActive() {
        List<Application> applications = applicationDao.getActive();
        for (Application application : applications) {
            commonAssertions(application);
            assertTrue(application.isStatus());
        }
    }

    @Test
    public void testGetDisplayable() {
        List<Application> applications = applicationDao.getDisplayable();
        for (Application application : applications) {
            commonAssertions(application);
            assertTrue(application.isDisplay());
        }
    }

    @Test
    public void testUpdate() {
        Application application = applicationDao.get(25);
        assertNotNull("Unable to get the initial record to update");
        assertFalse("found that the aka1 value was still set from the last test run", "unused".equals(application.getAka1()));

        application.setAka1("unused");
        int numUpdated = applicationDao.update(application);
        assertEquals(1, numUpdated);
    }

    @Test
    public void testInsert() {
        Application application = new Application();
        application.setName("Test Name");

        int id = applicationDao.insert(application);
        assertTrue(id > 0);
    }


    private void commonAssertions(Application application) {
        assertNotNull(application);
        assertTrue(application.getId() > 0);
        assertNotNull(application.getName());
        //note name (application) is the only required property
        //assertNotNull(application.getDescription());
        //assertNotNull(application.getEntityIds());
        //assertNotNull(application.getAcronym());
        //assertNotNull(application.getAka1());
        //assertNotNull(application.getAka2());
        //assertNotNull(application.getAka3());
        //assertNotNull(application.isStatus());
        //assertNotNull(application.getHelpGroup());
        //assertNotNull(application.getVersion());
        //assertNotNull(application.getDateUpdated());
        //assertNotNull(application.getVendor());
    }
}

The junit unit test class has a lot of things going on.

  • @RunWith(SpringJUnit4ClassRunner.class) – the makes the junit test class a spring bean, meaning that it can be injected with other spring managed beans
  • @TestExecutionListeners({TransactionalTestExecutionListener.class, DependencyInjectionTestExecutionListener.class}) – this enables the spring DI and the TX listeners.
  • @ContextConfiguration – this tells spring where the test application context is located. It defaults to the same package as the class, with the same name as the class, followed by -context.xml.
  • @Transactional – this tells spring that the test class methods should be wrapped in transactions, with the default value of automatic rollback at the end of the method.
  • @Autowired – indicates to spring that you want an object of ApplicationDao-type to be injected.
  • @Test – tells junit that this is a method that is part of the unit test

All that is needed now is the spring context file. This file for that test class is contained in ${basedir}/src/test/resources/norris/shelton/spring3junit4/TestApplicationDao-context.xml

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

    <!--
        Scan for spring beans in this package and all sub-packages
    -->
    <context:component-scan base-package="norris.shelton"/>

    <!--
        Scan for classes needing transactions.  Defaults to using a transaction manager object named transactionManager
    -->
    <tx:annotation-driven/>

    <!--
        Instantiate a transaction manager object for the specified datasource
    -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          p:dataSource-ref="bhsAuxCmsDataSource"/>

    <!--
        Create a pooled datasource.  On the production side, you would probably use the jee schema:
        <jee:jndi-lookup id="bhsAuxCmsDataSource" jndi-name="jdbc/some_jndi_name"/>
    -->
    <bean id="bhsAuxCmsDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
          p:driverClassName="net.sourceforge.jtds.jdbc.Driver"
          p:url="jdbc:jtds:sqlserver://server_ip/database_name"
          p:username="some_username"
          p:password="some_password"
          p:defaultAutoCommit="false"/>

    <!--
        Create a jdbc template object for the given datasource
    -->
    <bean id="bhsAuxCmsJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
          p:dataSource-ref="bhsAuxCmsDataSource"/>

    <!--
        Create a named parameter jdbc template object for the given datasource
    -->
    <bean id="bhsAuxCmsNamedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
        <constructor-arg ref="bhsAuxCmsDataSource"/>
    </bean>
</beans>

The maven project source is here

February 3rd, 2011

Posted In: Java, JUnit, Maven, Spring

Leave a Comment

Logging is needed, but all of the different frameworks makes this a pain. You are trying to research a problem and you have to bounce from log to log to research it.

SLF4J has bridge implementations that delegate to SLF4J APIs for commons-logging, log4j and even jdk logging.

You need to include the dependency for the logging API that you want to hijack as provided so they will not be packaged, then include the SLF4J bridge api. Repeat for each logging API you want to hijack, then include a SLF4J logging implementation, then away you go.

Here is how to hijack log4j

        <!--hijack log4j-->
        <!--NOTE Saying it is provided so it will not be packaged in my builds if a dependency is using it-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
            <scope>provided</scope>
        </dependency>

There are 3 strains of log4j (1.2, 1.3 & 2.0). None are compatible with any of the other. Luckily, only 1.2 is commonly used.

Here is how to hijack commons-logging

        <!--hijack commons logging-->
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
        </dependency>

There is also a jcl104-over-slf4j for commons-logging 1.0.x. I tried to be slick and include that on a project and it got ugly. Those versions of the commons-logging API must not be compatible.

I usually don’t hijack the jdk logging because I rarely encounter it.

All that is remaining is to include a SLF4J implementation. There is one for commons-logging and log4j, but my preferred solution is logback.

        <!-- LogBack -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>0.9.27</version>
        </dependency>

This is what works for me, but I see where there could be problems. The logging APIs that I am hijacking are in the compile classpath, but not in the deploy classpath. It could be possible to use Maven exclusions to totally strip out the artifacts, but you must put an exclusion on each dependency that uses the artifact. Good luck keeping up with that. They are supposed to fix this in maven 3.1

February 3rd, 2011

Posted In: Java, logback, slf4j

Tags: , , , , , , , ,

Leave a Comment

LinkedIn Auto Publish Powered By : XYZScripts.com