Search posterous

Search all posts and users. Type a name, type a favorite song title, whatever! See what comes up.
  

More posterous blogs











More recommended blogs »

Here are posterous posts filed under spring...

RGarg says...

SpringSource has launched a new free online training video: OSGi and Modular Applications. In this highly practical SpringSource training video Joris Kuipers will teach you how to use OSGi and modularization for Enterprise Java applications.

Filed under: Spring

chimski says...

It bloody warm, need to sleep!

Filed under: spring

trapo says...

Sometime ago I was fighting with Hibernate Validator, trying to implement an unique constraint and I could not figure out how to create one that requires database access:

The problem is that your validators can't access sessionFactory or any other interface to the database. I know that I can break some rules and instantiate a sessionFactory/session/connection directly inside the Validator, but I dislike to do wrong things consciously. So, I looking for alternatives.

Then, while studying the new validation support offered by Spring, I just discovery that using Spring I can inject any bean inside my ConstraintValidators. From Spring documentation:

By default, the LocalValidatorFactoryBean configures a SpringConstraintValidatorFactory that uses Spring to create ConstraintValidator instances. This allows your custom ConstraintValidators to benefit from dependency injection like any other Spring bean.

And here is how you can use that Spring feature to implement unique constraint.

THE UNIQUE ANNOTATION

It is very simple and direct. You can see details about implementing your custom annotations in Hibernate Validator docs.

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=UniqueConstraintValidator.class)
@Documented
public @interface Unique {
    String message() default "{constraints.unique}";
    Class<?> entity();
    String field();
    Class<?>[] groups() default {};   
    Class<? extends Payload>[] payload() default {};
}

You can also see the code at GitHub, where trapo is hosted. If I decide to change something, you can get an updated version there. Once you have the @Unique annotation, you must implement the ConstraintValidator referenced by "validationBy" property in @Constraint annotation above. Here is the code using a bean injected by Spring:

public class UniqueConstraintValidator implements ConstraintValidator<Unique, String> {

    @Autowired private SessionFactory sessionFactory;
   
    private Class<?> entity;
    private String field;
   
    public void initialize(Unique annotation) {
        this.entity = annotation.entity();
        this.field = annotation.field();
    }

    public boolean isValid(String value, ConstraintValidatorContext context) {
        if(StringUtils.isEmpty(value)) {
            return false;
        }
        return query(value).intValue() == 0;
    }

    private Number query(String value) {
        HibernateTemplate template = new HibernateTemplate(sessionFactory);
        DetachedCriteria criteria = forClass(entity)
                                   .add(eq(field, value))
                                   .setProjection(count(field));
        return (Number)template.findByCriteria(criteria).iterator().next();
    }

}

Just like the @Unique annotation, you can find the code in GitHub. Now you just need a Validator bean that can be injected in the classes that need it.

<bean id="validator"
      class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />

Voilá!

THE UGLY PART

Right now, Hibernate Validor don't offer any way to know from where your annotations are coming. If you take some time to think about the ConstraintValidator above, you will notice that I manually configure the entity class and field information that were annotated. I need that information to know what query I must execute in order to validate the uniqueness of the field. Bad to me. Really bad. Now I have to provide this information by myself annotating the field like the code highlighted below:

    @NotEmpty @Unique(entity = Forum.class, field = "name")
    private String name;

I can even hear the DRY concept crying! I can't also find a way to reflect this to my schema. Maybe Hibernate Validator can offer some interfaces that we can use to implement all this stuff.

Filed under: spring

trapo says...

I want to keep Trapo environment as simple as possible. I want that people just fire a command and all tests can run immediately: no setups, no database creation, no sql to run. Just one single command and trapo should do everything for you. Because of that, I start to isolate the test environment from the production one in order to use a different database instance (or even a different database vendor). To integrate the combo, I just call HSQLDB to the party. To avoid describe exactly what is HSQLDB, here is a quote from its homepage:

HSQLDB (HyperSQL DataBase) is the leading SQL relational database engine written in Java. It has a JDBC driver and supports a rich subset of ANSI-92 SQL (BNF tree format) plus many SQL:2008 enhancements. It offers a small, fast database engine which offers both in-memory and disk-based tables and supports embedded and server modes. Additionally, it includes tools such as a minimal web server, in-memory query and management tools (can be run as applets) and a number of demonstration examples.


FIRST, CONFIGURE MAVEN...

The simplest part: just add hsqldb as a maven dependency:

<dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <version>1.8.0.10</version>
    <scope>test</scope>
</dependency>

I decide to scope it as test because I'm not intent to use it in production, but you can use a different scope.

SECOND, ADD TEST RESOURCES...

Then, I just create two files inside src/test/resources directory. You can create files here to replace src/main/resources while running tests. But I want to reduce the amount of work to maintain both test and production enviroment, so, I'm replacing just what need to be replace: the database configuration. The two files are:

src/test/resources/hibernate.tests.properties

hibernate.connection.driver_class = org.hsqldb.jdbcDriver
hibernate.connection.url = jdbc:hsqldb:mem:trapo
hibernate.connection.username = sa
hibernate.connection.password =
hibernate.c3p0.min_size=5
hibernate.c3p0.max_size=20
hibernate.c3p0.timeout=1800
hibernate.c3p0.max_statements=50
hibernate.dialect = org.hibernate.dialect.HSQLDialect

hibernate.show_sql=true
hibernate.format_sql=true
hibernate.default_batch_fetch_size=5
hibernate.generate_statistics=true
hibernate.use_sql_comments=true
hibernate.jdbc.batch_size=10

hibernate.cache.use_second_level_cache=true
hibernate.cache.use_query_cache=true
hibernate.cache.provider_class=net.sf.ehcache.hibernate.EhCacheProvider
hibernate.cache.use_structured_entries=true

hibernate.hbm2ddl.auto=create-drop

Note the url property, it is pointing to an in memory database. Moreover, this files contains cache and some other optional configurations to mimic the production configurations. The other file is the Spring context to configure the SessionFactory for tests:

src/test/resources/applicationContextTest.xml

<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:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/util
           http://www.springframework.org/schema/util/spring-util-2.5.xsd">
  <bean id="testSessionFactory"
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
        p:annotatedClasses-ref="annotatedClasses"
        p:annotatedPackages="com.google.code.trapo.domain"
        p:hibernateProperties-ref="hibernateProperties" />
  <util:list id="annotatedClasses">
    <value>com.google.code.trapo.domain.Forum</value>
    <value>com.google.code.trapo.domain.Topic</value>
  </util:list>
  <util:properties id="hibernateProperties" location="classpath:hibernate.test.properties" />
</beans>

I'm not fully confortable with that because I need to copy and past mapped classes here. But, this is a start, so I prefer to have sub-optimal solution over don't have any solution at all.

THIRD, REFACTOR YOUR TESTS...

Your tests should use SpringJUnit4Runner instead of the default one. So you have to put the following annotation in the test class:

@RunWith(SpringJUnit4ClassRunner.class)
public class MyRepositoryTests {
    ...
}

You also need to say to Spring where it should find the configuration files using @ContextConfiguration annotation:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
    "classpath:/trapo-servlet.xml",
    "classpath:/applicationContextTest.xml"
})
public class MyRepositoryTests {
    ...
}

And, because your tests are using the database, is a good call configure them to be transactional. In order to do that, you need both @Transactional and @TransactionConfiguration annotations:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
    "classpath:/trapo-servlet.xml",
    "classpath:/applicationContextTest.xml"
})
@Transactional
@TransactionConfiguration(transactionManager = "txManager")
public class MyRepositoryTests {
    ...
}

And then inject the testSessionFactory configure in applicationContextTests.xml in your tests:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
    "classpath:/trapo-servlet.xml",
    "classpath:/applicationContextTest.xml"
})
@Transactional
@TransactionConfiguration(transactionManager = "txManager")
public class MyRepositoryTests {

    @Autowired private SessionFactory testSessionFactory;

}

FINALLY, JUST RUN YOUR TESTS...

And now the grand finale, just open a command prompt, go to trapo directory and type:

mvn clean test

If you want to see cobertura report, type:

mvn clean cobertura:cobertura

Hope that helps.

Filed under: spring

Quân says...

- Modify service layer 

modify the SimpleProductManager and add a reference to a ProductDao
'springapp/service/SimpleProductManager.java'

- remove the productManager configuration and the list of products from the springapp-servlet.xml
- configure the service layer in its own application context file 'applicationContext.xml'.
It will be loaded via a servlet listener that we will define in 'web.xml'

- 'springapp/web/WEB-INF/applicationContext.xml'

- using AOP (Aspect Oriented Programming) in the form of a transaction advice and an AspectJ pointcut to define where the transactions should
be applied

The pointcut applies to any method called on the ProductManager interface. The advice is a transaction advice that applies to methods with a name starting with 'save'.
- using the DBCP connection pool

- add lib spring-aspects & dbcp in dom.xml

- run & enjoy

Click here to download:
springapp.rar (13 KB)

Filed under: Spring

Quân says...

- Using mySQL 

- Create database startup script
'springapp/db/create_products.sql'

'springapp/db/load_data.sql'

Run scripts and load test data

- Create a Data Access Object (DAO) implementation for JDBC
'springapp/repository/ProductDao.java'

'springapp/repository/JdbcProductDao.java'
+ using Spring's JDBC, don't have to worry about opening and closing
the connection or any statements, won't have to catch any exceptions
+ SimpleJdbcDaoSupport provides SimpleJdbcTemplate

+ getSimpleJdbcTemplate().query: provide the SQL statement and a
class that can handle the mapping between the ResultSet and the
Product class
+ ProductMapper implement ParameterizedRowMapper: mapRow map the
data from each row into a class
+ MapSqlParameterSource allows us to use named parameters instead of
the typical "?"

- Add a private field named 'id' complete with setters and getters to Product.java

- Implement tests for JDBC DAO implementation
Add Spring test framework

JdbcProductDaoTests.java

+ extending AbstractTransactionalDataSourceSpringContextTests --> load application context (test-content.xml)
+ onSetUpInTransaction: clear table & load data test, rolled back once the test finishes
test/resources/test-context.xml

test/resources/jdbc.properties

- Run unit test

Click here to download:
springapp.rar (14 KB)

Filed under: Spring

Quân says...

we add a form that will allow the user to enter a percentage price increase 

'springapp/web/WEB-INF/jsp/priceincrease.jsp'

'springapp/service/PriceIncrease.java'

'springapp/service/PriceIncreaseValidator.java'

add an entry in the 'springapp-servlet.xml' file to define the new
form and controller
 + define objects for commandClass and validator
 + formView and a successView
'springapp/web/WEB-INF/springapp-servlet.xml'

controller for this form
 + onSubmit(..) method: get command object
'springapp/web/PriceIncreaseFormController.java'

adding some messages to the 'messages.properties' resource file
'springapp/web/WEB-INF/classes/messages.properties'

add a link to the price increase page from the 'hello.jsp'

re--deploy & run

Click here to download:
springapp.rar (11 KB)

Filed under: Spring

Quân says...

Add reference to business logic in the controller

- rename HelloController to InventoryController and the HelloControllerTests to InventoryControllerTests
- modify the InventoryController to hold a reference to the ProductManager class
- add code to have the controller pass some product information to the view.
'/springapp/web/InventoryController.java'

Modify the view to display business data and add support for message bundle
- <c:forEach/>: displays product information
- <fmt:message/>: pulls the text to display from a provided 'message' source
'springapp/web/WEB-INF/jsp/hello.jsp'

Add some test data
add a SimpleProductManager to our configuration file and to pass that into the setter of the InventoryController
put the data in 'springapp-servlet.xml' (remember to rename the reference to HelloController to InventoryController)
'springapp/web/WEB-INF/springapp-servlet.xml'

add the 'messageSource' bean entry
'springapp/web/WEB-INF/classes/messages.properties'

Rebuild & run app

Click here to download:
springapp.rar (9 KB)

Filed under: Spring

Quân says...

- Business:
The application we will be building from scratch over the course of this tutorial is a very basic inventory management system.
In our inventory management system, we have the concept of a product and a service for handling them. In particular, the business has requested the ability to increase prices across all products.
The validation rules for price increase are:
 The maximum increase is limited to 50%.
 The minimum increase must be greater than 0%.
Find below a class diagram of our inventory management system.
- Create Product class
Let's also make it Serializable, not necessary for our application, but could come in handy later on when we persist and store its state.
'springapp/domain/Product.java'

Create the ProductManager
It contains two methods:
  increasePrice(): increases prices for all products
  getProducts(): retrieving all products
'springapp/service/ProductManager.java'
SimpleProductManager class that implements the ProductManager interface
'springapp/service/SimpleProductManager.java'
- SimpleProductManagerTests.java

Click here to download:
springapp.rar (7 KB)

Filed under: Spring

Quân says...

- using the JSP Standard Tag Library (JSTL) 

pom.xml
 

- create the header file for inclusion in all the JSPs we create.
'springapp/web/WEB-INF/jsp/include.jsp'

- update 'index.jsp' to use JSTL, redirecting to our front Controller.
'springapp/web/index.jsp'

- Move 'hello.jsp' to the 'WEB-INF/jsp' directory. Using the JSTL tag to output variable retrieved from the model
'springapp/web/WEB-INF/jsp/hello.jsp'

- Update the location of the JSP in our controller.
update our unit test class first

test fail

- Update HelloController

- Test again
- Run http://localhost:8084/springapp/ --> redirect to /hello.htm
- Decouple the view from the controller
map to the view using a logical name, remove prefix and suffix
'springapp/web/WEB-INF/springapp-servlet.xml'

update the view name in the controller test & controller to 'hello'
- test & run again

Click here to download:
springapp.rar (5 KB)

Filed under: Spring