Aspect Oriented Programming: Spring

One of the most powerfull tools spring provides us with is aspect orientation.
Aspect oriented programming helps us to continue development for special use cases, without breaking our modularity.

This is part of a series of blog posts on aspect orientation using different tools.
The first one is about aspect orientation on Spring.

In this example I will display the behaviour of two beans from the same definition but each one has a different aspect.

The employe class will be used to create the beans for this project.

package com.gkatzioura.spring.employees;

public class Employee {

    public void enterWorkArea() {

    }

    public void leaveWorkArea() {

    }

}

However considering a company, probably there are more than one types of employes.
For our project we will use the aspect of the supervisor employee and the aspect of the worker employee.

The worker employee before entering the work area should inform the supervisor employee that he has arrived.
The supervisor employee before entering the area should check that everything is ok.

package com.gkatzioura.spring.employees;

import org.apache.log4j.Logger;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class WorkerBeforeAdvice implements MethodBeforeAdvice {

    private static final Logger LOGGER = Logger.getLogger(WorkerBeforeAdvice.class);

    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {

        if (method.getName().equals("enterWorkArea")) {
            System.out.println("Informing the supervisor that I have arrived");
        }

    }

}
package com.gkatzioura.spring.employees;

import org.apache.log4j.Logger;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class SupervisorBeforeAdvice implements MethodBeforeAdvice {

    private static final Logger LOGGER = Logger.getLogger(SupervisorBeforeAdvice.class);

    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        
        if(method.getName().equals("enterWorkArea")) {
            System.out.println("Check if everything is ok");
        }
    }
}

The advices defined above would have to be wired on our employee bean according to the aspect needs.
For the worker bean we will use the WorkerBeforeAdvice before entering the factory.
For the employee bean we wil use the SupervisorBeforeAdvice before leaving the factory

On your META-INF create a spring directory and inside the spring directory create the employeeapects.xml file. This file would wire the employee bean with the behaviours described above.
The first ProxyFactory Bean will be used to create employee beans wired with the WorkerBeforeAdvice behaviour.
The second ProxyFactory Bean will be used to create employee beans wired with the SupervisorBeforeAdvice behaviour.

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

    <bean id="employee" class="com.gkatzioura.spring.employees.Employee"/>

    <bean id="workerBeforeBean" class="com.gkatzioura.spring.employees.WorkerBeforeAdvice"/>
    <bean id="supervisorBeforeBean" class="com.gkatzioura.spring.employees.SupervisorBeforeAdvice"/>

    <bean id="workerProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="employee"/>
        <property name="interceptorNames">
            <list>
                <value>workerBeforeBean</value>
            </list>
        </property>
    </bean>

    <bean id="superVisorProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="employee"/>
        <property name="interceptorNames">
            <list>
                <value>supervisorBeforeBean</value>
            </list>
        </property>
    </bean>

</beans>

On your dispatcher-servlet.xml you need to import employeeapects.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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <context:component-scan base-package="com.gkatzioura.spring"/>

    <import resource="classpath:/META-INF/spring/employeeapects.xml"/>
    <mvc:annotation-driven/>
</beans>

We will create a controller in order to create the beans.

package com.gkatzioura.spring.controllers;

import com.gkatzioura.spring.employees.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class AspectController {

    @Autowired
    @Qualifier("workerProxy")
    Employee workEmployee;

    @Autowired
    @Qualifier("superVisorProxy")
    Employee superVisorEmployee;

    @RequestMapping(value = "/")
    @ResponseBody
    public String index() {

        return "Check other endpoints";
    }

    @RequestMapping(value = "/worker/enter")
    @ResponseBody
    public String workerEnter() {
        workEmployee.enterWorkArea();
        return "Worker entered the factory";
    }

    @RequestMapping(value = "/supervisor/enter")
    @ResponseBody
    public String superVisortEnter() {
        superVisorEmployee.enterWorkArea();
        return "Supervisor entered the factory";
    }

}

You should hit the endpoint /worker/enter and /supervisor/enter.
You will notice that the workerEmployee and superVisorEmployee have a different reaction once calling the enterWorkArea function.

By hitting the /worker/enter endpoint, the string “Informing the supervisor that I have arrived” is printed on the console.
By hitting the /supervisor/enter endpoint, the string “Check if everything is ok” is printed on the console.

Advertisement

Make objects with node.js

Node.js is definitely awesome, especially on an i/o heavy application.
Being a Java developer, my first take was on making a class.
ResultExtractor.js would be the file used to define a module.

function ResultExtractor(wordToMatch) {
    this.wordToMatch = wordToMatch
}

ResultExtractor.prototype.filterRecords = function(jsonResults) {
    var filteredRecords = []

    for(var i=0;i<jsonResults.length;i++) {
        var jsonResult = jsonResults[i]
        if(this.recordContains(jsonResult))
            filteredRecords.push(jsonResult)
    }

    return filteredRecords
}

ResultExtractor.prototype.recordContains = function(jsonResult) {
    return jsonResult['word'].indexOf(this.wordToMatch)!=-1;
}

module.exports = ResultExtractor

On your other file that imports the module you can use the ResultExtractor class


var ResultExtractor = require('./ResultExtractor')

var resultExtractor = new ResultExtractor('whatever')
var testData = [{'word':'whatever'},{'word':'where'},{'word':'when'}]
var filteredData = resultExtractor.filterRecords(testData)

console.log(filteredData)

That’s it!!! More post will follow, node.js seems really promising.

Database refactoring with Liquibase

Database changes are painful. Every time you have to do it, there is a mess of alter table statements and sql scripts.
What I really liked about Liquibase is its maven plugin.
Consider the scenario where each colleague has a database for testing purposes on his local pc.
Once you have pushed to the repository your Liquibase configuration files It is a matter of a maven command in order for your
colleagues to be on the same page with you.

Add inside your pom add the plugin configuration

<build>
	<resources>
		<resource>
			<directory>src/main/resources</directory>
			<filtering>true</filtering>
		</resource>
	</resources>
	<plugins>
		<plugin>
			<groupId>org.liquibase</groupId>
			<artifactId>liquibase-maven-plugin</artifactId>
			<version>3.0.5</version>
			<configuration>
				<driver>{your driver}</driver>
				<url>jdbc:mysql://127.0.0.1:3306/test</url>
				<username>test</username>
				<password>test</password>
				<changeLogFile>database/refactor/db.changelog.master.xml</changeLogFile>
			</configuration>
			<executions>
				<execution>
					<phase>process-resources</phase>
					<goals>
						<goal>update</goal>
					</goals>
				</execution>
			</executions>
		</plugin>
	</plugins>
</build>

Our changelog file would be place on the resources

src/main/resources/database/refactor/db.changelog.master.xml

We can include other changelog files inside one

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
		xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog db.changelog-3.1.xsd">
	<include file="db.changelog-1.0.xml" relativeToChangelogFile="true" />
	<include file="db.changelog-2.0.xml" relativeToChangelogFile="true" />
	<include file="db.changelog-3.0.xml" relativeToChangelogFile="true" />
	<include file="db.changelog-4.0.xml" relativeToChangelogFile="true" />
	<include file="db.changelog-5.0.xml" relativeToChangelogFile="true" />
</databaseChangeLog>

db.changelog-1.0.xml creates a table

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog 
		xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog db.changelog-3.1.xsd">
	<changeSet id="1" author="change1">
		<createTable tableName="supervisor">
			<column name="username" type="varchar(50)" />
			<column name="password" type="varchar(50)"/>
			<column name="securePassword" type="varchar(50)"/>
		</createTable>
	</changeSet>
</databaseChangeLog>

db.changelog-2.0.xml creates another table

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
		xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog db.changelog-3.1.xsd">
	<changeSet id="2" author="change2">
		<createTable tableName="employee">
			<column name="username" type="varchar(50)"/>
			<column name="password" type="varchar(50)"/>
		</createTable>
	</changeSet>
</databaseChangeLog>

db.changelog-3.0.xml makes an insert

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
		xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog db.changelog-3.1.xsd">
	<changeSet id="3" author="change3">
		
		<insert tableName="employee">
			<column name="username" value="emmanouil"/>
			<column name="password" value="whatever"/>
		</insert>

	</changeSet>
</databaseChangeLog>

db.changelog-4.0.xml is a showcase of insert but with a precondition

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog db.changelog-3.1.xsd">
<changeSet id="4" author="change4">
	<preConditions>
		<sqlCheck expectedResult="0">select count(*) from supervisor</sqlCheck>
	</preConditions>
	<insert tableName="supervisor">
		<column name="username" value="james"/>
		<column name="password" value="test"/>
	</insert>
</changeSet>
</databaseChangeLog>

db.changelog-5.0.xml adds a unique constraint

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
		xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog db.changelog-3.1.xsd">
	<changeSet id="5" author="change5">
		<addUniqueConstraint tableName="supervisor" columnNames="username"/>
	</changeSet>
</databaseChangeLog>

In order to run just issue

mvn resources:resources liquibase:update

Liquibase has some great features. If you do iterative development and you do a lot of refactoring it is great for the collaboration among the team members.