Intivism

February 12, 2010

Google launches Chart API

Filed under: Uncategorized — Omri Ben Shitrit @ 9:42 am

Google announced yesterday the launching of Chart Tools.
I think it would be very interesting to see the data visualizations that will come out of the API as they are embedded into Google’s spreadsheet, etc.
Google divided the API into image charts, which are rendered by the Google server, and interactive charts, which uses a browser side java script library.

February 10, 2010

Step by step unit testing with Spring 3 JSR303 validation support using eclipse and maven

Filed under: Spring, eclipse, java, jsr 303, junit 4, maven2, test driven development — Omri Ben Shitrit @ 9:06 pm

In this post I would like to show a few things:
1. How Eclipse and Maven work well together to easily set up projects using Spring 3 and other libraries (maven by example)
2. Spring’s support in JSR 303 – Validation API
3. Unit testing using JSR 303 validation API
4. Using auto wiring to provide Data Fixture for unit testing using eclipse (Thanks to Martin Harris)

We start off by creating a new project (assuming you have latest eclipse installed and the maven 2 eclipse plugin):
image

choose a simple project (skipping the archetype selection).
Archetype are by definition “an original pattern or model from which all other things of the same kind are made”.
This basically means maven provides us with a set of predefined project structures (what is the directory tree, which files exists in the project, etc) we can choose from, according to our task – are we creating a jar? a webapp?
These well known tasks follow a usually common project structure which is predefined by maven.
For now we will skip this and go for the simple project.

image

Maven, as a project dependency tool amongst other things, allows to define different artifacts as part of a larger group of work.
For now, we only have our demo application of a lunch restaurants review system.

image

Once the project has been created, you will notice the following directory structure:
image
The important things to notice:

  1. Separate source folders for our regular code and testing code
  2. Separate resource folders
  3. The pom.xml file which contains the maven dependencies

Once we click the pom.xml we will get the plugin view of the file:

image

I have introduced a property into the file called org.springframework.version, and gave it the value 3.0.0.RELEASE, for spring’s latest release.
I got this information from the maven repository meta data on spring frame work here.
The meta data also provides the group ID and artifact ID of the spring core project (just as an example):

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

   2: <metadata>

   3:     <groupId>org.springframework</groupId> 

   4:     <artifactId>spring-core</artifactId> 

   5:     <version>2.0</version> 

   6:     <versioning>

   7:         <release>2.0.8</release> 

   8:         <versions>

   9:             <version>2.0</version> 

  10:             <version>2.0.1</version> 

  11:             <version>2.0.2</version> 

  12:             <version>2.0.3</version> 

  13:             <version>2.0.4</version> 

  14:             <version>2.0.5</version> 

  15:             <version>2.0.6</version> 

  16:             <version>2.0.7</version> 

  17:             <version>2.0.8</version> 

  18:             <version>2.5</version> 

  19:             <version>2.5.1</version> 

  20:             <version>2.5.2</version> 

  21:             <version>2.5.3</version> 

  22:             <version>2.5.4</version> 

  23:             <version>2.5.5</version> 

  24:             <version>2.5.6</version> 

  25:             <version>3.0.0.RELEASE</version> 

  26:         </versions>

  27:         <lastUpdated>20091216174158</lastUpdated> 

  28:     </versioning>

  29: </metadata>

If you will switch to the dependencies view, you will be able to quickly add a dependency using the artifact ID, group ID and versioning.

image

Notice the use of the property we added before for the spring version.

Once we added a dependency and save the file, Maven will automatically download the jar files and add it to our build path in the project.

We can invoke this manually as well by right clicking on the project –> Maven –> Update Dependencies.

image

The full dependency file (pom.xml) I used for spring 3.0.0 is this:

   1: <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">

   2:   <modelVersion>4.0.0</modelVersion>

   3:   <groupId>com.lab49</groupId>

   4:   <artifactId>LunchApp</artifactId>

   5:   <name>Lunch App</name>

   6:   <version>0.0.1-SNAPSHOT</version>

   7:   <description>Helps you choose your lunch!</description>

   8:   <repositories>

   9:       <repository>

  10:           <id>jboss</id>

  11:           <url>http://repository.jboss.com/maven2/</url>

  12:       </repository>

  13:   </repositories>

  14:   <dependencies>

  15:       <dependency>

  16:           <groupId>org.springframework</groupId>

  17:           <artifactId>spring-core</artifactId>

  18:           <version>${org.springframework.version}</version>

  19:       </dependency>

  20:       <dependency>

  21:           <groupId>org.springframework</groupId>

  22:           <artifactId>spring-expression</artifactId>

  23:           <version>${org.springframework.version}</version>

  24:       </dependency>

  25:       <dependency>

  26:           <groupId>org.springframework</groupId>

  27:           <artifactId>spring-beans</artifactId>

  28:           <version>${org.springframework.version}</version>

  29:       </dependency>

  30:       <dependency>

  31:           <groupId>org.springframework</groupId>

  32:           <artifactId>spring-aop</artifactId>

  33:           <version>${org.springframework.version}</version>

  34:       </dependency>

  35:       <dependency>

  36:           <groupId>org.springframework</groupId>

  37:           <artifactId>spring-context</artifactId>

  38:           <version>${org.springframework.version}</version>

  39:       </dependency>

  40:       <dependency>

  41:           <groupId>org.springframework</groupId>

  42:           <artifactId>spring-tx</artifactId>

  43:           <version>${org.springframework.version}</version>

  44:       </dependency>

  45:       <dependency>

  46:           <groupId>org.springframework</groupId>

  47:           <artifactId>spring-jdbc</artifactId>

  48:           <version>${org.springframework.version}</version>

  49:       </dependency>

  50:       <dependency>

  51:           <groupId>org.springframework</groupId>

  52:           <artifactId>spring-orm</artifactId>

  53:           <version>${org.springframework.version}</version>

  54:       </dependency>

  55:       <dependency>

  56:           <groupId>org.springframework</groupId>

  57:           <artifactId>spring-oxm</artifactId>

  58:           <version>${org.springframework.version}</version>

  59:       </dependency>

  60:       <dependency>

  61:           <groupId>org.springframework</groupId>

  62:           <artifactId>spring-context-support</artifactId>

  63:           <version>${org.springframework.version}</version>

  64:       </dependency>

  65:       <dependency>

  66:           <groupId>org.springframework</groupId>

  67:           <artifactId>spring-web</artifactId>

  68:           <version>${org.springframework.version}</version>

  69:       </dependency>

  70:       <dependency>

  71:           <groupId>org.springframework</groupId>

  72:           <artifactId>spring-webmvc</artifactId>

  73:           <version>${org.springframework.version}</version>

  74:       </dependency>

  75:       <dependency>

  76:           <groupId>org.springframework</groupId>

  77:           <artifactId>spring-webmvc-portlet</artifactId>

  78:           <version>${org.springframework.version}</version>

  79:       </dependency>

  80:       <dependency>

  81:           <groupId>org.springframework</groupId>

  82:           <artifactId>spring-test</artifactId>

  83:           <version>${org.springframework.version}</version>

  84:           <scope>test</scope>

  85:       </dependency>

  86:       <dependency>

  87:           <groupId>javax.validation</groupId>

  88:           <artifactId>validation-api</artifactId>

  89:           <version>1.0.0.GA</version>

  90:       </dependency>

  91:       <dependency>

  92:           <groupId>junit</groupId>

  93:           <artifactId>junit</artifactId>

  94:           <version>4.7</version>

  95:       </dependency>

  96:       <dependency>

  97:           <groupId>org.hibernate</groupId>

  98:           <artifactId>hibernate-validator</artifactId>

  99:           <version>4.0.2.GA</version>

 100:       </dependency>

 101:       <dependency>

 102:           <groupId>org.slf4j</groupId>

 103:           <artifactId>slf4j-api</artifactId>

 104:           <version>1.5.6</version>

 105:       </dependency>

 106:       <dependency>

 107:           <groupId>org.slf4j</groupId>

 108:           <artifactId>slf4j-log4j12</artifactId>

 109:           <version>1.5.6</version>

 110:       </dependency>

 111:       <dependency>

 112:           <groupId>log4j</groupId>

 113:           <artifactId>log4j</artifactId>

 114:           <version>1.2.15</version>

 115:       </dependency>

 116:   </dependencies>

 117:   <dependencyManagement>

 118:       <dependencies>

 119:       </dependencies>

 120:   </dependencyManagement>

 121:   <properties>

 122:       <org.springframework.version>3.0.0.RELEASE</org.springframework.version>

 123:     </properties>

 124: </project>

Now we will add a few classes to our domain, namely an Address class and a Review class.

We will annotate these fields using the JSR 303 Validation Annotations, and I will use the following ones:

  1. @NotNull – marks that a field cannot be null
  2. @Size – enforces size restrictions on a collection or a String’s length
  3. @Min – a minimal value on field
  4. @Max – a maximal value on a field
  5. @Pattern – a regular expression on a String field  (taken from http://regexlib.com/)
   1: package com.lab49.lunchapp.domain;

   2:  

   3: import javax.validation.constraints.NotNull;

   4: import javax.validation.constraints.Pattern;

   5: import javax.validation.constraints.Size;

   6:  

   7: public class Address {

   8:  

   9:     @NotNull

  10:     @Size(min = 1, max = 30)

  11:     private String street;

  12:     

  13:     @NotNull

  14:     @Size(min = 0, max = 30)

  15:     private String houseNumber;

  16:     

  17:     @NotNull

  18:     @Size(min = 1, max = 30)

  19:     private String city;

  20:     

  21:     @NotNull

  22:     @Size(min = 0, max = 30)

  23:     @Pattern(regexp = "(^(?!0{5})(\\d{5})(?!-?0{4})(-?\\d{4})?$)")

  24:     private String zipCode;

  25:     

  26:     @NotNull

  27:     @Size(min = 1, max = 30)

  28:     @Pattern(regexp = "^(?-i:A[LKSZRAEP]|C[AOT]|D[EC]|F[LM]|G[AU]|HI|I[ADLN]|K[SY]|LA|M[ADEHINOPST]|N[CDEHJMVY]|O[HKR]|P[ARW]|RI|S[CD]|T[NX]|UT|V[AIT]|W[AIVY])$")

  29:     private String state;

  30:     

  31:     @NotNull

  32:     @Size(min = 1, max = 30)

  33:     private String country;

  34:     

  35:     public Address() {

  36:         

  37:     }

  38:  

  39:     public String getStreet() {

  40:         return street;

  41:     }

  42:  

  43:     public void setStreet(String street) {

  44:         this.street = street;

  45:     }

  46:  

  47:     public String getHouseNumber() {

  48:         return houseNumber;

  49:     }

  50:  

  51:     public void setHouseNumber(String houseNumber) {

  52:         this.houseNumber = houseNumber;

  53:     }

  54:  

  55:     public String getCity() {

  56:         return city;

  57:     }

  58:  

  59:     public void setCity(String city) {

  60:         this.city = city;

  61:     }

  62:  

  63:     public String getZipCode() {

  64:         return zipCode;

  65:     }

  66:  

  67:     public void setZipCode(String zipCode) {

  68:         this.zipCode = zipCode;

  69:     }

  70:  

  71:     public String getState() {

  72:         return state;

  73:     }

  74:  

  75:     public void setState(String state) {

  76:         this.state = state;

  77:     }

  78:  

  79:     public String getCountry() {

  80:         return country;

  81:     }

  82:  

  83:     public void setCountry(String country) {

  84:         this.country = country;

  85:     }

  86: }

   1: package com.lab49.lunchapp.domain;

   2:  

   3: import java.util.Date;

   4:  

   5: import javax.validation.constraints.Max;

   6: import javax.validation.constraints.Min;

   7: import javax.validation.constraints.NotNull;

   8: import javax.validation.constraints.Size;

   9:  

  10: public class Review {

  11:     @NotNull

  12:     @Size(min = 1, max = 30)

  13:     private String title;

  14:     

  15:     @NotNull

  16:     @Size(min = 1, max = 1000)

  17:     private String body;

  18:     

  19:     @Min(1)

  20:     @Max(5)

  21:     private int starRating;    

  22:     

  23:     @NotNull

  24:     private Date date;

  25:     

  26:     public Date getDate() {

  27:         return date;

  28:     }

  29:  

  30:     public void setDate(Date date) {

  31:         this.date = date;

  32:     }

  33:  

  34:     public Review() {

  35:         

  36:     }

  37:     

  38:     public String getTitle() {

  39:         return title;

  40:     }

  41:  

  42:     public void setTitle(String title) {

  43:         this.title = title;

  44:     }

  45:  

  46:     public String getBody() {

  47:         return body;

  48:     }

  49:  

  50:     public void setBody(String body) {

  51:         this.body = body;

  52:     }

  53:  

  54:     public int getStarRating() {

  55:         return starRating;

  56:     }

  57:  

  58:     public void setStarRating(int starRating) {

  59:         this.starRating = starRating;

  60:     }

  61: }

Now we can start connecting the dots.

We create a applicationContext-lunchApp.xml file in src/main/resources:

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

   2: <beans xmlns="http://www.springframework.org/schema/beans"

   3:     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   4:     xmlns:aop="http://www.springframework.org/schema/aop"

   5:     xmlns:context="http://www.springframework.org/schema/context"

   6:     xmlns:jdbc="http://www.springframework.org/schema/jdbc"

   7:     xmlns:tx="http://www.springframework.org/schema/tx"

   8:     xmlns:util="http://www.springframework.org/schema/util"

   9:     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

  10:         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd

  11:         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd

  12:         http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd

  13:         http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd

  14:         http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">

  15:     

  16:     <context:component-scan base-package="com.lab49"/>

  17:     

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

  19:  

  20: </beans>

We only have two elements in our beans element:

  1. A Context Component scan element which tells spring to scan 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).
  2. We configure the LocalValidatorFactoryBean as spring’s default JSR 303 validator.

    Keep in mind that this will trigger the default bootstrap mechanism and spring will look for a JSR 303 implementation, such as Hibernate’s, so you need to make sure its in the classpath.

    We do not need to worry about it because it is part of the dependencies in pom.xml, therefore maven will automatically download the jar for us.

For the actual unit testing, we will use spring’s build JUnit support, theTestContext Framework offers full integration with JUnit 4.5+ through a custom runner.

All we need to do is annotate classes with @Runwith(SpringJUnit4ClassRunner.class).

By doing so we can code JUnit unit testing and integration testing, and enjoy various benefits from the TestContext such as dependency injection in our tests and transactional test methods among other things.

As a side note, If we do not specify an empty @TestExecutionListeners({}) annotation to disable the default listeners, our test runner would require an ApplicationContext to be configured through @ContextConfiguration.

This is not the case for us.

We can now move forward with creating a base abstract class for our tests:

   1: package com.lab49.lunchapp.testutilities;

   2:  

   3: import org.junit.runner.RunWith;

   4: import org.springframework.test.context.ContextConfiguration;

   5: import org.springframework.test.context.TestExecutionListeners;

   6: import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

   7: import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;

   8:  

   9: @RunWith(SpringJUnit4ClassRunner.class)

  10: @ContextConfiguration(locations = { "classpath:applicationContext-lunchApp.xml" })

  11: @TestExecutionListeners({DependencyInjectionTestExecutionListener.class})

  12: public class AbstractContextBaseTest {

  13:  

  14: }

This class will be our base class for other unit tests and through spring will load our context and inject our dependencies.

For the JSR 303 validation of our beans in the unit tests, I will introduce another base generic utility class.

This class will be injected with the validator instance from our context by spring, and through the validation API will validate a bean. The validation is done by checking how many constraint violations are in a bean.

The generics helps us avoid casts.

   1: package com.lab49.lunchapp.testutilities.validation;

   2:  

   3: import static org.junit.Assert.assertTrue;

   4:  

   5: import java.util.Set;

   6:  

   7: import javax.validation.ConstraintViolation;

   8: import javax.validation.Validator;

   9:  

  10: import org.apache.log4j.Logger;

  11: import org.springframework.beans.factory.annotation.Autowired;

  12:  

  13: import com.lab49.lunchapp.testutilities.AbstractContextBaseTest;

  14:  

  15: public class AbstractValidationTest<T> extends AbstractContextBaseTest {

  16:     

  17:     private static final Logger LOGGER = Logger.getLogger(AbstractValidationTest.class);

  18:     

  19:     @Autowired

  20:     protected Validator validator;

  21:     

  22:     public Set<ConstraintViolation<T>> validate(T t) {

  23:         return validator.validate(t);

  24:     }

  25:     

  26:     public void assertValid(T t) {

  27:         assertTrue(isValid(t));

  28:     }

  29:     

  30:     public void assertInvalid(T t) {

  31:         assertTrue(!isValid(t));

  32:     }

  33:  

  34:     private boolean isValid(T t) {

  35:         Set<ConstraintViolation<T>> violations = validate(t);

  36:  

  37:         for(ConstraintViolation<T> violation : violations) {

  38:             LOGGER.debug(violation);

  39:         }

  40:         return (violations.size() == 0);

  41:     }

  42:  

  43: }

As described in the blog post I mentioned in the beginning, we will use Spring to inject our data fixtures into the actual unit test. First, lets create a fixture:

   1: @Component

   2: public class AddressFixture {

   3:     public static final String ZIP_CODE = "10028";

   4:     public static final String STREET = "2nd Avenue";

   5:     public static final String STATE = "NY";

   6:     public static final String HOUSE_NUMBER = "1565";

   7:     public static final String COUNTRY = "USA";

   8:     public static final String CITY = "New York";

   9:     

  10:     public Address createAddressFixture() {

  11:         Address address = new Address();

  12:         address.setCity(CITY);

  13:         address.setCountry(COUNTRY);

  14:         address.setHouseNumber(HOUSE_NUMBER);

  15:         address.setState(STATE);

  16:         address.setStreet(STREET);

  17:         address.setZipCode(ZIP_CODE);

  18:         

  19:         return address;

  20:     }

  21: }

We annotate our fixture with the @Component annotation. This marks our class as a candidate for auto-detection when using annotation-based configuration and classpath scanning.

And finally our test which we name AddressTest after the Address class in our domain package:

   1: public class AddressTest extends AbstractValidationTest<Address> {

   2:     

   3:     @Autowired

   4:     private AddressFixture addressFixture;

   5:     private Address address;

   6:     

   7:     @Before

   8:     public void setUp() {

   9:         address = addressFixture.createAddressFixture();

  10:     }

  11:     

  12:     @Test

  13:     public void testStreet_cannotBeNull() {

  14:         address.setStreet(null);

  15:         assertInvalid(address);

  16:     }

  17:     

  18:     @Test

  19:     public void testStreet_cannotBeEmpty() {

  20:         address.setStreet("");

  21:         assertInvalid(address);

  22:     }

  23:     

  24:     @Test

  25:     public void testStreet_cannotBeTooLong() {

  26:         address.setStreet("123456789123456789123456789123456789");

  27:         assertInvalid(address);

  28:     }

  29:     

  30:     @Test

  31:     public void testStreet_legalValue() {

  32:         address.setStreet("123");

  33:         assertValid(address);

  34:     }

  35:     

  36:     @Test

  37:     public void testZipcode_illegalValue() {

  38:         address.setZipCode("myzipcode");

  39:         assertInvalid(address);

  40:     }

  41:     

  42:     @Test

  43:     public void testZipcode_legalValue() {

  44:         address.setZipCode(AddressFixture.ZIP_CODE);

  45:         assertValid(address);

  46:     }

  47:     

  48:     @Test

  49:     public void testState_legalValue() {

  50:         address.setState(AddressFixture.STATE);

  51:         assertValid(address);

  52:     }

  53:     

  54:     @Test

  55:     public void testState_illegalValue() {

  56:         address.setState("MyState");

  57:         assertInvalid(address);

  58:     } 

  59: }

Before I move on, I would like to point out a very cool feature of the maven plugin to download javadoc.

Say we face the following situation where we would like to know what the annotation means but we miss the javadoc:

image 
We can just go to the JAR file under Maven dependencies and using the plugin download the javadoc:

image

Going back to our unit test, the Autowired tells spring to resolve the reference to the AddressFixture according to the classes known to its context. Since we marked AddressFixture as @Component and we have component scanning on, spring is aware of the existence of such a matching bean.

The tests enforce the business rules we have on the address object (legal US zipcode value for instance).

Though I’ve showed you the Address class prior to the test class, the way I actually did it was to write the AddressTest class test first, and then added the proper annotation to make the test pass, in accordance with Test Driven Development.

Last but not least we can have a suite class to run several tests that check our domain’s correctness:

   1: package com.lab49.lunchapp.suite;

   2:  

   3: import org.junit.runner.RunWith;

   4: import org.junit.runners.Suite;

   5:  

   6: import com.lab49.lunchapp.domain.AddressTest;

   7: import com.lab49.lunchapp.domain.ReviewTest;

   8:  

   9: @RunWith(Suite.class)

  10: @Suite.SuiteClasses({AddressTest.class, ReviewTest.class})

  11: public class DomainSuite {

  12: // yes, this is empty

  13: }

And the outcome:

image

February 5, 2010

Condition constraints using Spring’s new expression language

Filed under: Spring, Spring Expression Language, java, programming — Omri Ben Shitrit @ 4:52 pm

After being exposed to Spring Expression Language, I thought it could be useful to harness its power to describe condition constraints on methods, as part of a growing focus on test driven development.

The idea is to describe expectations from the object’s state prior to the method’s invocation, expectation from the input and expectation from state following the method’s invocation.
Since spring expression language exposes public methods, its interesting to see how we can verify correctness using other public methods.

For this purpose I will introduce 3 simple annotations:

@ConditionContext- a spring expression to be evaluated prior to any condition checks
@Precondition – a spring expression to be evaluated prior to the method invocation and after the latter
@Postcondition – a spring expression to be evaluated after the method invocation

All of these annotations are of the same code structure:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Postcondition {
String value();
}

I’ve written a very simple service that just manages a list of products.
The service supports the following operations:

public interface FooService {

public Set<Product> getProducts();

public Product getProduct(int id);

public void insertProduct(Product product);

public void updateProduct(Product product);

public void deleteProduct(Product product);

}

Where a Product just contains an ID and price.

Before I show the behind the scenes, I would like to show the final outcome which is what I described above:

@ConditionContext("#ctx.put('sizeBefore', #self.Products.size())")
@Precondition("#arg0 != null and #arg0.Id &gt; 0")
@Postcondition("#sizeBefore + 1 == #self.Products.size() and " +
"#self.Products.?[Id == 1].size() == 1 and " +
"#self.getProduct(#arg0.Id).equals(#arg0)")
public voidinsertProduct(Product product) {
products.add(product);
}

Here are my expectations from the insertProduct method as described in the constraint (just as an example, i know there many other possible constraints which can be expressed):

As a precondition I want the input to be valid which means the product argument (arg0) is not and has a positive ID.
I can also express conditions about the service’s state prior to insert, I will show that in the post condition.
Following the insert the number of products should be incremented by 1, and I shall be able to find the added product.
The latter is expressed in two different ways:
1) the obvious one of invoking getProduct() on the Id and making sure the objects are equal.
2) getting the entire collection and finding it in the collection. I have also wanted to demonstrate SpEL, so I used the collection selection syntax it provides.
The condition context is simply a way to store information prior to method invocation.
Since you cant set variables outside the SpEL context, I used a map to store the data and I later introduce the map’s keys as variable in the SpEL context.
This is why in @Postcondition you see #sizeBefore.
How does this work?

I started by creating a small driver code:

ApplicationContext ctx = newClassPathXmlApplicationContext(newString[]{"beans.xml"});
FooService fooService = (FooService) ctx.getBean("fooService");
Product product1 = new Product();
product1.setId(1);
product1.setPrice(1);
fooService.insertProduct(product1);

Adding my AOP and bean definitions in beans.xml file:

<!-- Enabling aspect support -->
<aop:aspectj-autoproxy/>
<bean id="product" class="com.lab49.spelaspecttest.domain.Product"/>
<bean id="fooService" class="com.lab49.spelaspecttest.service.DefaultFooService"/>
<bean id="aspectBean" class="com.lab49.spelaspecttest.aspect.PointCuts"/>

 

I created an aspect with the following:

  1. A Pointcut for any public method.
  2. An Around advice (such that I can control method invocation) to handle the pre and post condition logic.
    The logic is quite simple, evaluate the preconditions, if they are met execute the method, if not throw an IllegalStateException.
    Following the method execution, evaluate the post conditions and do the same.

The real magic occurs in the aspect code as follows (see documentation in code):

@Aspect
public class PointCuts {

	@Pointcut("execution(public * *(..))")
	private void anyPublicOperation() {}

	@Pointcut("within(com.lab49.spelaspecttest..*)")
	private void inServiceLayer() { }

	/**
	 * Just for debugging, print methods executed in service layer
	 * @param jp
	 */
	@Before("inServiceLayer()")
	public void logServiceMethodExecution(JoinPoint jp) {
		System.out.println("Executing: " + jp.getSignature().getName());
	}	

	/**
	 * I use the public pointcut and add target and annotation to statically type the annotation variables and targen object (the service).
	 *
	 */
	@Around(value = "anyPublicOperation() && target(bean) && @annotation(conditionContext) && @annotation(precondition) && @annotation(postcondition)", argNames="bean, conditionContext, precondition, postcondition")
	public Object checkConditionConstraints(ProceedingJoinPoint pjp, Object bean, ConditionContext conditionContext, Precondition precondition, Postcondition postcondition) throws Throwable {
		System.out.println("Condition context: " + conditionContext.value());
		System.out.println("Precondition: " + precondition.value());
		System.out.println("Postcondition: " + postcondition.value());

		// SPeL action begins
		EvaluationContext context = new StandardEvaluationContext(bean);
		// I add a #self variable because the implicit this SPeL evaluates properties
		// with no object against can be confusing or inconsistent.
		context.setVariable("self", bean);
		// Add all the #arg* variables to the context, the method inputs.
		addArgumentsToContext(pjp, context);

		ExpressionParser parser = new SpelExpressionParser();
		Expression preConditionExp = parser.parseExpression(precondition.value());

		// a little syntactic sugar - we turn the keys in the map in the condition context
		// to variables in evaluating the precondition and postcondition expressions
		updateConditionContext(conditionContext, parser, context);

		// SPeL uses the class<T> generics to ease on casting return values
		boolean preconditionValue = preConditionExp.getValue(context, Boolean.class);
		System.out.println("Precondition value: " + preconditionValue);

		if(!preconditionValue) {
			throw new IllegalStateException("Precondition " + precondition.value() + " failed");
		}

		// use the JoinPoint to actually execute the underlying method
		Object retval = pjp.proceed();

		Expression postConditionExp = parser.parseExpression(postcondition.value());
		boolean postConditionValue = postConditionExp.getValue(context, Boolean.class);
		System.out.println("Postcondition value: " + postConditionValue);

		if(!postConditionValue) {
			throw new IllegalStateException("Postcondition " + postcondition.value() + " failed");
		}

		//if the postcondition is met, return the method's original return value
		return retval;
	}

	private void updateConditionContext(ConditionContext conditionContext, ExpressionParser parser, EvaluationContext context) {
		Map<String,Object> map = new HashMap<String, Object>();
		context.setVariable("ctx", map);
		Expression contextExpression = parser.parseExpression(conditionContext.value());
		contextExpression.getValue(context);

		for(Entry<String, Object> entry : map.entrySet()) {
			context.setVariable(entry.getKey(), entry.getValue());
		}
	}

	private void addArgumentsToContext(ProceedingJoinPoint pjp, EvaluationContext context) {
		Object[] args = pjp.getArgs();
		for(int i = 0; i < args.length; i++) {
			context.setVariable("arg" + i, args[i]);
		}
	}
}

July 27, 2009

Multi language XMLs using XSLT and Saxon

Filed under: programming — Tags: , , , , — Omri Ben Shitrit @ 5:51 pm

I’ve recently encountered a need to support translating an XML into different languages.
While solutions exist to use XSLT to statically transform XML pages using a “language file” my need was a little bit different.
I needed to connect an existing translation module to be used as the translations provider, and not a static XML file.
The translation API basically accepts a key for the translation, and a list of properties which specify where to look for the translation (for example, primary language, secondary language, project name, etc).

Lets look at the static way.

translation.xml:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="multilangfile.xsl"?>
<translations>
<translation key="test">Intivism</translation>
</translations>

Now lets look at multilangfile.xsl:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:key name="translate" match="translation" use="@key" />
<xsl:template match="/">
<html>
<body>
<xsl:value-of select="key('translate', 'test')" />
</body>
</html>
</xsl:template>
</xsl:stylesheet>

We use xsl:key function to search for our appropriate  key in the static xml file.

The dynamic way:

  1. We use saxon as an XSLT java framework. Saxon allows linking between java code and XSLT custom functions.
  2. We do not specify a static xml file. We specify an empty XML file as input source when using saxon API.
  3. We create our own XSLT function in our own name space, which is linked to our back end existing translation module.

Our multilangfile.xsl will now look like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:myfunc="http://myfunc.intivism.com" xmlns:java="http://saxon.sf.net/java-type" exclude-result-prefixes="java" xmlns:sls="java:/com.intivism.TranslateMethods">

<xsl:param name="primaryLang"/>
<xsl:param name="projectName"/>

<xsl:function  name="myfunc:translate">
<xsl:param name="key"/>
<xsl:variable name="result">
<sls:translate primaryLang="$primaryLang" projectName="$projectName" key="$key" xsl:extension-element-prefixes="sls"/>
</xsl:variable>
<xsl:value-of select="$result"/>
</xsl:function>

We will supply values to the xsl:param’s list in runtime through the transformer object (code coming up).
These will be our run time attributes for obtaining the right translation from the existing module.
The TranslateMethods class will be our entry point for handling the operations within our sls xsl name space.

import net.sf.saxon.style.ExtensionElementFactory;

public class TranslateMethods implements ExtensionElementFactory {
@Override
public Class getExtensionClass(String name) {
if(name.equals("translate")) {
return TranslationOperationElement.class;
}

return null;
}
}

While parsing the XSL, saxon will invoke our class method to get the appropriare class to handle operations in our name space.
The TranslationOperationElement class extends ExtensionInstruction and has two important methods to implement: compile and prepareAttributes.
Lets look at the class:

public class TranslationOperationElement extends ExtensionInstruction{

private Expression m_primaryLangAtt;
        private Expression m_projectNameAtt;
private Expression m_keyAtt;

@Override
public Expression compile(Executable exec) throws XPathException {
return new TranslationOperationInstruction(m_primaryLangAtt, m_projectNameAtt, m_keyAtt);
}

@Override
protected void prepareAttributes() throws XPathException {
String primaryLangAtt= getAttributeValue("", "primaryLang");
        if (primaryLangAtt==null) {
            reportAbsence("primaryLang");
            primaryLangAtt= "";
        }
        m_primaryLangAtt = makeExpression(primaryLangAtt);
       // Same for other attributes
}

private static class TranslationOperationInstruction extends SimpleExpression {

public TranslationOperationInstruction(Expression primaryLangAtt, Expression projectNameAtt, Expression keyAtt) {
Expression[] subs = {primaryLangAtt, projectNameAtt, keyAtt};
setArguments(subs);
}

public int getImplementationMethod() {
            return Expression.EVALUATE_METHOD;
        }

        public int computeCardinality() {
            return StaticProperty.EXACTLY_ONE;
        }

        public String getExpressionType() {
            return "sls:translate";
        }

        public Item evaluateItem(XPathContext context) throws XPathException {
               // this is where we connect to our translation module. The returning item will be the result of our translation.
            String primarylang = arguments[0].evaluateAsString(context).toString();
            String projectName = arguments[1].evaluateAsString(context).toString();
            String key = arguments[2].evaluateAsString(context).toString();

            return new ObjectValue(TranslationModule.translate(primaryLang, projectName, key));
        }
}
}

Putting it all together:

		TransformerFactory tFactory = TransformerFactory.newInstance();
		Transformer transformer = tFactory.newTransformer(new StreamSource("ourmultilangfile.xsl"));
                // give runtime value to our XSL parameters
		transformer.setParameter("projectName", "someValue");
		transformer.setParameter("primaryLang", "someValue");

		transformer.transform(new StreamSource("d:\\someemptyxml.xml"), new StreamResult(new FileOutputStream("output.xml")));

Powered by WordPress