Friday, 8 February 2008

@Configurable example with Spring 2.5

Hey,
My first blog! I feel somewhat late on the blogging scene...but better late than never!

I recently did a simple (maybe too simple..i hope to add more features) example of using Spring 2.5 @Configurable. The example shows how to configure your domain objects (normally outside the control of the container,for example objects returned by Hibernate) so that dependencies can be injected by Spring.

Now I know there are a lot of opinions on why you would do this (Personally I like the idea of having DomainServices which aren't related to application services or infrastructure services). We can have this debate later but for those of you who are interested in doing this here are the following code samples.


DomainObject.java [package com.amin.spring.app.domain]

@Configurable(dependencyCheck=true)
public class DomainObject {
private String name;
private DomainService domainService;
@Autowired
public void setDomainService(DomainService domainService) {
this.domainService = domainService;
}

public DomainService getDomainService() {
return domainService;
}
public void setName(String name) {
boolean isValidName = domainService.isValidUserName(name);
if (!isValidName) {
throw new UsernameValidationException("username already used. please try alternative");
}
this.name = name;
}

public String getName() {
return this.name;
}
}


DomainService.java [package com.amin.spring.app.service]

public interface DomainService {
public boolean isValidUserName(String name);
}

DomainServiceImpl.java[package com.amin.spring.app.service] [the implementation class is simple! But it's to give you an idea]

@Service
public class DomainServiceImpl implements DomainService {
public boolean isValidUserName(String name) {
boolean isValidUserName = StringUtils.hasText(name); return isValidUserName;
}
}

conf/spring/bean.xml [snippet excluding xml declaration..be sure to use 2.5]

<context:annotation-config />
<context:spring-configured />
<context:load-time-weaver />
<context:component-scan base-package="com.amin.spring.app" />

Amazing...that's it!


DomainObjectTest.java [src/test/java - com.amin.spring.app.domain]

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/conf/spring/bean.xml"})
public class DomainObjectTest {

@Test
public void testServiceInjectionToDomainObject() throws Exception {
DomainObject domainObject = new DomainObject();
assertNotNull("Domain Service was not set", domainObject.getDomainService());
}

@Test
@ExpectedException(value=java.lang.RuntimeException.class)
public void testServiceInjectionToDomainObjectWithValidationException() throws Exception {
DomainObject domainObject = new DomainObject();
domainObject.setName(null);
assertNotNull("Validation Service was not set", domainObject.getDomainService());

}
}



Right...for this example I am using Load Time Weaving (you can do with build time weaving with Maven or Ant) which will enable AspectJ to weave classes that are marked with @Configurable, allowing Spring to inject any dependencies into domain object before it is returned to the calling code. In order to do this you will need to configure the run configuration of the testcase (i do this via eclipse). This can be done by adding the following to the JVM argument:
-javaagent:/spring-agent.jar

And then you're ready to go....

Feel free to comment on the blog and/or on the example code.

17 comments:

Unknown said...

Good stuff! Nice to see you blogging finally :)

Unknown said...

My only comment is that it is not obvious that DomainObject.java is in package com.amin.spring.app.
Besides that it's clear, cool and useful.
I'm waiting for more :)

Andy Morris said...

Nice quick and easy read. Good first blog!

Think I'd argue the case against using the technique for creating self-persistent objects (not necessarily what you were advocating!). Used carefuly though it looks quite handy!

Will this also provide a simple approach to aspectizing your domain objects?

aminmc said...

Injecting of domain objects with Hibernate Sessions is being done in Grails. If this kind of behaviour can be done in Grails why not in Java?

I think when you have a simple domain model with CURD operations then letting your domain objects have more behaviour is ok. Spring and AspectJ provides a simple yet effective mechanism for aspectizing your domain objects so why not do it?

Yes i can see your point about clear separation of domain, dao and services but do you really need to have a dao when all you do is create, update, and delete domain objects?

I think we need to start thinking outside of the box. J2EE design patterns are useful but they can overcomplicate simple situations.

Andy Morris said...

Firstly I wouldn't really consider the simple service/dao approach as a jee pattern. I see J2ee patterns as more complicated and tending to be work arounds to ejb design flaws.

Have you considered the effects of self persistent domain objects being placed in http sessions? Particuarly when things like session replication come into play - not just performance issues but potentially an impact on behaviour. I guess you could always proxy to these services...

I also wouldn't like to see either client code having access to methods like 'save()' or objects 'servicing' themselves.

If a requirement was so simple it required just CRUD (how many are there actually like this) then I probably wouldn't recommend using Java in the first place! You'd probably use something like groovy / ruby / rails / whatever the latest alternative or variant is.

aminmc said...

Ok Andy...thanks for your input.

nfma said...

Good stuff Amin.

A simple example that makes it easy to grasp.
This is yet another good tool that might be very handy.

Just keep posting this pearls.

Unknown said...

I'm a bit concerned about the (dependency) layering in this example. I'm not sure how this would scale for big Enterprise applications.
But hey - it is just an example...

Stephan Westen

Unknown said...

I'm trying to get this example to work, but having trouble because I'm not sure what JARs are being used and what the full Spring XML header looks like.

I would be very cool if you included a download of the full source please!

I really want to get this working!

aminmc said...

Hi Chris

Can you provide me with an email address, I can send a small maven project which you should give you more information. If this isn't possible I'll add all the relevant information (hopefully tomorrow).

Cheers

Unknown said...

Could you show your xml declaration? The example isn't working for me, and I hope that's the reason.

A minor note. The following is not correct:
@Test
@ExpectedException(value=java.lang.RuntimeException.class)
Instead, it should be @ExpectedException(value=UsernameValidationException.class)
The former is too broad, and allows the test to pass when setName() throws a NullPointerException because of the missing domainService.

aminmc said...

Hi Chris

Yes the expected exception should be updated to be UsernameValidationException as the runtime exception is too broad. Can you tell me what exception you are getting? Is the domain service not being injected?

As I mentioned last time can you provide me with your email address and I can send you all the files you need. The configuration that I posted hasn't changed from ones that I am using now.

Cheers

Unknown said...

Yes, the domainService is not being injected.

In
testServiceInjectionToDomainObject the assertion fails, and in testServiceInjectionToDomainObjectWithValidationException I get a NullPointerException because the domainService is null on this line:

boolean isValidName = domainService.isValidUserName(name);

My email address is groovyflow@gmail.com. If you get the chance, please send the files.

By the way, I'm not Chris. :) I suppose he never sent you his email address.

aminmc said...

You can get a maven project showing the use of @Configurable at:

http://www.mediafire.com/?sharekey=1ec24e63bc904fca2fb2ca15d7ea42d9e04e75f6e8ebb871

HTH

aminmc said...

Hi

Sorry there seems to be an issue with the attachment not being visible, please try this:
http://www.mediafire.com/?moyzyn01mdi

If there any issues please let me know.

Achilles Ram Nakirekanti said...

PLEASE HELP ME SIR: my mail id is nakirekantisaikumar@gmail.com
please:
................................................................
public class CarSalon {
//...
public void testDrive() {
Car car = new Car();
car.startCar();
}
}

@Configurable(preConstruction = true)
@Component
public class Car {

@Autowired
private Engine engine;
@Autowired
private Transmission transmission;

public void startCar() {
transmission.setGear(1);
engine.engineOn();

System.out.println("Car started");
}
}


@Component
public class Engine {
//...
}

@Component
public class Transmission {
//...
}

In Schema i added: beans, context,aop
1.Sir i wrote this is code(with all required methods).
2.i added this jar to the build path "spring-aspects.jar"
3.i added the apsectj jars(aspjectjar,aspjectweaver,aspectjtimer)to the build path.
4.am using eclipse, then i added spring-instrument.3.X.X verstion to the VM argments in eclipse.
with the syntax: -javaagent:"PATH to the folder\spring-instrument-x.x.x.jar"
PROBLEM: when run the code am getting errors
PLEASE HELP ME SIR: my mail id is nakirekantisaikumar@gmail.com

Achilles Ram Nakirekanti said...

With correction:

PLEASE HELP ME SIR: my mail id is nakirekantisaikumar@gmail.com
please:
................................................................
public class CarSalon {
//...
public void testDrive() {
Car car = new Car();
car.startCar();
}
}

@Configurable(preConstruction = true)
@Component
public class Car {

@Autowired
private Engine engine;
@Autowired
private Transmission transmission;

public void startCar() {
transmission.setGear(1);
engine.engineOn();

System.out.println("Car started");
}
}


@Component
public class Engine {
//...
}

@Component
public class Transmission {
//...
}

In Schema i added: beans, context,aop
1.1: I Added all jars in spring framework version 3.x-x and commons-logging.
1.Sir i wrote this is code(with all required methods).
2.i added this jar to the build path "spring-aspects.jar"
3.i added the apsectj jars(aspjectjar,aspjectweaver,aspectjtimer)to the build path.
4.am using eclipse, then i added spring-instrument.3.X.X verstion to the VM argments in eclipse.
with the syntax: -javaagent:"PATH to the folder\spring-instrument-x.x.x.jar"
PROBLEM: when run the code am getting errors
PLEASE HELP ME SIR: my mail id is nakirekantisaikumar@gmail.com