Java Ninja Chronicles By Norris Shelton

Things I learned in the pursuit of code

We had an existing Springframework web application that needed to handle images being stored in AmazonWS. Here are the changes taht I needed to make to add the new functionality.

  1. Add the AmazonWS Maven dependency
            <dependency>
                <groupId>com.amazonaws</groupId>
                <artifactId>aws-java-sdk</artifactId>
                <version>1.3.30</version>
            </dependency>
    
  2. Create the files that contain the AmazonWS credentials. Ours are stored in a webdav repository and we have different credentials per environment. To differentiate the files, I used the naming convention of amazon-.properties. The contents of the files were:
    # Fill in your AWS Access Key ID and Secret Access Key
    # http://aws.amazon.com/security-credentials
    accessKey=<nunya bidness>
    secretKey=<also nunya bidness>
    
  3. I created a Spring java configuration class.. Since our files are stored on a webdav and there is a webdav for each environment, I need to jump through some hoops in order to get the properties file.
    package com.appriss.jxp.amazon;
    
    import com.amazonaws.auth.PropertiesCredentials;
    import com.amazonaws.services.s3.AmazonS3Client;
    import com.appriss.justicexchange.commons.http.HttpUtils;
    import com.appriss.justicexchange.commons.utility.Environment;
    import com.appriss.justicexchange.commons.utility.Utility;
    import org.apache.commons.io.IOUtils;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.io.IOException;
    
    /**
     * Spring java configuration class for AmazonWS.
     * @author nshelton
     */
    @Configuration
    public class AmazonConfiguration {
    
        /**
         * Creates an Amazon S3 Client.
         * @return AmazonS3Client
         */
        @Bean
        public AmazonS3Client amazonS3Client() throws IOException {
            PropertiesCredentials propertiesCredentials = new PropertiesCredentials(
                IOUtils.toInputStream(
                    HttpUtils.getResponse(
                        Utility.getWebdavServer() + "/product/jxportal/config/amazon-" + Environment.getValue() + ".properties")));
            return new AmazonS3Client(propertiesCredentials);
        }
    }
    
  4. The Junit test class for this is very simple. Did it create a spring bean of the desired type.
    package com.appriss.jxp.amazon;
    
    import com.amazonaws.services.s3.AmazonS3Client;
    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 static org.junit.Assert.*;
    
    /**
     * Tests the Amazon configuration class.
     * @author nshelton
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
    @ContextConfiguration
    public class TestAmazonConfiguration {
    
        @Autowired
        private AmazonS3Client amazonS3Client;
    
        @Test
        public void testAmazonS3Client() {
            assertNotNull(amazonS3Client);
        }
    }
    
  5. We are going to use AmazonWS to create temporary URLs that will expire after a set amount of time. The timeout value is stored in another file on the webdav and is fronted by a PropertiesDao class that caches the value for a set amount of time so it will not bombard the webdav with too many requests, yet allows it to be changed at runtime. I extracted this logic into a method in a utility class.
    package com.appriss.jxp.amazon;
    
    import com.amazonaws.HttpMethod;
    import com.amazonaws.services.s3.AmazonS3Client;
    import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
    import com.appriss.jxp.properties.PropertiesDao;
    import org.joda.time.MutableDateTime;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.io.IOException;
    
    /**
     * Amazon utility class.
     * @author nshelton
     */
    @Component
    public class AmazonUtility {
    
        @Autowired
        private PropertiesDao propertiesDao;
    
        @Autowired
        private AmazonS3Client amazonS3Client;
    
        /**
         * Gets an AmazonWS url for the given resource.
         * @param bucket the value of the AmazonWS bucket
         * @param siteId the siteId of this request
         * @param s3FileName the name of the S3 resource to generate an expiring URL for
         * @return the expiring AmazonWS URL for the requested resource
         * @throws IOException
         */
        public String getUrl(String bucket, String siteId, String s3FileName) throws IOException {
            GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucket + siteId,
                                                                                                      s3FileName,
                                                                                                      HttpMethod.GET);
            MutableDateTime mutableDateTime = new MutableDateTime();
            mutableDateTime.add(Long.parseLong(propertiesDao.getProperty("amazonws.timeout")));
            generatePresignedUrlRequest.setExpiration(mutableDateTime.toDate());
    
            return amazonS3Client.generatePresignedUrl(generatePresignedUrlRequest).toString();
        }
    }
    
  6. Just to make things fun, where I need to use this is legacy code and doesn’t know anything about Springframework. The desired logic is for it to use the AmazonWS S3 to create URLs if the image has been stored there and IF that URL resolution has been turned on. It had been requested to make it possible for the use of AmazonWS to be turned off if the service isn’t responsive enough.
        public String getFormattedSigImageURL() {
            String formattedSigImageURL = null;
            // if I have the information needed to construct an AmazonWS URL
            if (StringUtils.isNotBlank(s3SignatureBucket) &amp;&amp; StringUtils.isNotBlank(s3ImageName)) {
                PropertiesDao propertiesDao = CommonTools.getBean(PropertiesDao.class);
                try {
                    // if the AmazonWS service is turned on
                    if ("true".equals(propertiesDao.getProperty("amazonws.enabled"))) {
                        AmazonUtility amazonUtility = CommonTools.getBean(AmazonUtility.class);
                        formattedSigImageURL = amazonUtility.getUrl(s3SignatureBucket, pharmacySiteId, s3ImageName);
                    }
                } catch (IOException ignore) {}
            }
    
            // not found, try another way
            if (StringUtils.isBlank(formattedSigImageURL)) {
                try {
                    formattedSigImageURL = CommonTools.getImageURL(sigImageURL);
                } catch (NamingException ignore) {
                    formattedSigImageURL = sigImageURL;
                }
            }
    
            return formattedSigImageURL;
        }
    

February 15th, 2013

Posted In: Java, JUnit, Maven, Spring

Leave a Comment

The project finally got so large that the JUnit tests would run out of heap space during the tests. I added space to the IDEA maven runner, but that didn’t work. You have to add it to the maven-surefire plugin.

<plugin>                                            
    <groupId>org.apache.maven.plugins</groupId>     
    <artifactId>maven-surefire-plugin</artifactId>  
    <version>2.12.3</version>                       
    <configuration>                                 
        <argLine>-Xms512m -Xmx512m</argLine>        
    </configuration>                                
</plugin>                                           

September 11th, 2012

Posted In: Java, JUnit, Maven

Leave a Comment

This is what you get when you try to test code that relies on Java EE APIs. You can compile the code because the javaee;javaee-api;5 dependency has stubs for the methods. Unfortunately, it does not contain implementations for the methods which causes your tests to blow-up. You get around this by including the implementation BEFORE tha javaee5 dependency.

Glassfish 2

<dependency>
     <groupId>net.java.dev.glassfish</groupId>
     <artifactId>glassfish-persistence-api</artifactId>
     <version>b32g</version>
     <scope>test</scope>
</dependency>

Glassfish 3

<dependency>
     <groupId>org.glassfish.extras</groupId>
     <artifactId>glassfish-embedded-all</artifactId>
     <version>3.0.1</version>
     <scope>test</scope>
</dependency>

April 9th, 2012

Posted In: Java, JUnit, Maven

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

Strange Maven dependency resolution problems. How Springframework caused problems when they absorbed the APIs into an existing dependency.

java.lang.NoSuchMethodError: org.springframework.transaction.interceptor.TransactionAttribute.getQualifier()Ljava/lang/String;
	at org.springframework.test.context.transaction.TransactionalTestExecutionListener.beforeTestMethod(TransactionalTestExecutionListener.java:149)
	at org.springframework.test.context.TestContextManager.beforeTestMethod(TestContextManager.java:374)
	at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73)
	at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
	at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
	at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:35)
	at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:146)
	at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:97)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.apache.maven.surefire.booter.ProviderFactory$ClassLoaderProxy.invoke(ProviderFactory.java:103)
	at $Proxy0.invoke(Unknown Source)
	at org.apache.maven.surefire.booter.SurefireStarter.invokeProvider(SurefireStarter.java:145)
	at org.apache.maven.surefire.booter.SurefireStarter.runSuitesInProcess(SurefireStarter.java:87)
	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:69)

What the heck is this? Your library has this class and the method does exist and even takes a String. What makes it even weirder is that your tests run in your editor, but do not run from the Maven command line. How can this be?

Unknown to you, one of the dependencies that you are using is pulling spring-dao-2.0.x.jar (usually version 2.0.8, but not always). OK, but what is the problem with that? The code that was in that jar was absorbed by the spring-tx jar in the spring 2.5. There is no replacement for that jar, so even though you are using a much newer version of Spring, nothing says not to pull the old jar into your project. It is up to you to find any dependency that uses the jar and exclude it.

In Maven, it is done like this:

        <dependency>
            <groupId>com.bhsi</groupId>
            <artifactId>dao-user</artifactId>
            <version>1.1</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-dao</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

This has to be done for any dependency that has that jar as a transitive dependency.

Once that is done, you need to see if you have the replacement. In my specific case, my test class ran sometimes, but did not run other times. This was due to having spring-dao-2.0.8.jar and spring-tx-3.0.4-RELEASE.jar in my project classpath. If you don’t have the spring-tx jar, add it and you should be good to go.

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>

January 31st, 2011

Posted In: Java, JUnit, Maven, Spring

3 Comments

« Previous Page
LinkedIn Auto Publish Powered By : XYZScripts.com