Thứ Hai, 22 tháng 12, 2014

Castle Windsor: How to Register Components

You can register your components in the following ways:
  • Registering components one-by-one (Read More)
  • Registering components by conventions (Read more)
  • Registering components using Xml configuration, which can be combined with the code options (Read more)

Registering components one-by-one

1
2
3
4
5
6
// Initialize the container
var container = new WindsorContainer();
// Register your component with the desired lifestyle
container.Register(Component.For<IComponent>()
                       .ImplementedBy<Component>()
                       .LifestylePerThread());

What about open generic types?

1
2
3
4
5
// Initialize the container
var container = new WindsorContainer();
// Register your component for instance with the default lifestyle = Singleton
container.Register(Component.For(typeof (IRepository<>)
                       .ImplementedBy(typeof (Repository<>));

How to replace an allready registered component?

In Windsor you can simple register it again, the last registered component will be the one used

1
2
3
4
5
6
7
8
9
10
// Initialize the container
var container = new WindsorContainer();
// Register your component for instance with a lifestyle
container.Register(Component.For(typeof (IRepository<>)
                       .ImplementedBy(typeof (Repository<>)
                       .LifestylePerWebRequest());
// Register a specialized CustomerRepository
container.Register(Component.For<IRepository<Customer>>()
                       .ImplementedBy<CustomerRepository>()
                       .LifestylePerWebRequest());

How to make one class resolvable by two interface but have them share the same instance?

1
2
3
4
5
6
// Initialize the container
var container = new WindsorContainer();
// Register your component
container.Register(Component.For<IRepository<Customer>, ICustomerRepository>()
                       .ImplementedBy<CustomerRepository>()
                       .LifestylePerWebRequest());

How can i use the decorator pattern?

First let’s create a logging decorator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class LoggingCustomerRepository : IRepository<Customer>
{
   public ILogger Logger { get; set; };
   public IRepository<Customer> Repository { get; private set; }
   public LoggingCustomerRepository(IRepository<Customer> repository)
   {
      this.Repository = repository;
   }
   public Customer this[int id]
   {
      get { return Repository[id]; }
   }
   public void Add(Customer instance)
   {
      logger.Debug("Adding customer");
      Repository.Add(instance);
   }
}
With Castle Windsor the order of the registrations enables this behavior, so the first implementation will be injected into the decorator.

1
2
3
4
5
6
7
8
9
10
// Initialize the container
var container = new WindsorContainer();
// Register the default implementation
container.Register(Component.For<IRepository<Customer>()
                       .ImplementedBy<CustomerRepository>()
                       .LifestylePerWebRequest());
// Now register the decorator
container.Register(Component.For<IRepository<Customer>()
                       .ImplementedBy<LoggingCustomerRepository>()
                       .LifestylePerWebRequest());

Registering components by conventions

To do exactly the same but with conventions syntax

1
2
3
4
5
6
7
// Initialize the container
var container = new WindsorContainer();
// Register all non abstract class inheriting from IRepository with all interfaces
// as service, so resolvable by all interfaces
container.Register(Classes.FromThisAssembly()
                       .BasedOn(typeof(IRepository<>))
                       .WithServiceAllInterfaces());
WithServiceAllInterfaces: Means windsor will register the component bound to all it’s interfaces, so if for instance your CustomerRepository implements IRepository but also ICustomerRepository, when you resolve an instance, it will be shared across both contracts for the specified lifetime ( transient means no sharing )

Using installers

Installers provide you a way to group related registrations into one class, to create an installer simply create a class and implement IWindsorInstaller, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
namespace Windsor.Tests.Generics
{
   public class RepositoryInstaller : IWindsorInstaller
   {
      public void Install(IWindsorContainer container, IConfigurationStore store)
      {
         container.Register(Classes.FromThisAssembly()
                               .BasedOn(typeof(IRepository<>))
                               .WithServiceAllInterfaces());
      }
   }
}
To use this installer simple install it on your container instance

1
2
3
4
// Initialize the container
var container = new WindsorContainer();
// Install the installer(s)
container.Install(new RepositoryInstaller());

Thứ Tư, 3 tháng 12, 2014

Spring MVC and the HATEOAS constraint

HATEOAS is a REST architecture principle where hypermedia is used to change application state. To change state, the returned resource representation contains links thereby 'constraining' the client on what steps to take next.

The Spring-HATEOAS project aims to assist those writing Spring MVC code in the creation of such links and the assembly of resources returned to the clients. 

The example below will cover a simple scenario showing how links are created and returned for the resource, Bet. Each operation on the resource is described below:

  • createBet - this POST operation will create a Bet.
  • updateBet - this PUT operation will update the Bet.
  • getBet - this GET operation will retrieve a Bet.
  • cancelBet - this DELETE operation will cancel the Bet.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
@Controller
@RequestMapping("/bets")
public class BetController {
 
 private BetService betService;
 private BetResourceAssembler betResourceAssembler;
 
 public BetController(BetService betService,
   BetResourceAssembler betResourceAssembler) {
  this.betService = betService;
  this.betResourceAssembler = betResourceAssembler;
 }
 
 @RequestMapping(method = RequestMethod.POST)
 ResponseEntity createBet(@RequestBody Bet body) {
  Bet bet = betService.createBet(body.getMarketId(),
    body.getSelectionId(), body.getPrice(), body.getStake(),
    body.getType());
  BetResource resource = betResourceAssembler.toResource(bet);
  return new ResponseEntity(resource, HttpStatus.CREATED);
 }
 
 @RequestMapping(method = RequestMethod.PUT, value = "/{betId}")
 ResponseEntity updateBet(@PathVariable Long betId,
   @RequestBody Bet body) throws BetNotFoundException, BetNotUnmatchedException {
  Bet bet = betService.updateBet(betId, body);
  BetResource resource = betResourceAssembler.toResource(bet);
  return new ResponseEntity(resource, HttpStatus.OK);
 }
 
 @RequestMapping(method = RequestMethod.GET, value = "/{betId}")
 ResponseEntity getBet(@PathVariable Long betId) throws BetNotFoundException {
  Bet bet = betService.getBet(betId);
  BetResource resource = betResourceAssembler.toResource(bet);
  if (bet.getStatus() == BetStatus.UNMATCHED) {
   resource.add(linkTo(BetController.class).slash(bet.getId()).withRel("cancel"));
  }
  return new ResponseEntity(resource, HttpStatus.OK);
 }
 
 @RequestMapping(method = RequestMethod.GET)
 ResponseEntity> getBets() {
  List betList = betService.getAllBets();
  List resourceList = betResourceAssembler.toResources(betList);
  return new ResponseEntity>(resourceList, HttpStatus.OK);
 }
 
 @RequestMapping(method = RequestMethod.DELETE, value = "/{betId}")
 ResponseEntity cancelBet(@PathVariable Long betId) {
  Bet bet = betService.cancelBet(betId);
  BetResource resource = betResourceAssembler.toResource(bet);
  return new ResponseEntity(resource, HttpStatus.OK);
 }
 
 @ExceptionHandler
 ResponseEntity handleExceptions(Exception ex) {
  ResponseEntity responseEntity = null;
  if (ex instanceof BetNotFoundException) {
   responseEntity = new ResponseEntity(HttpStatus.NOT_FOUND);
  } else if (ex instanceof BetNotUnmatchedException) {
   responseEntity = new ResponseEntity(HttpStatus.CONFLICT);
  } else {
   responseEntity = new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
  }
  return responseEntity;
 }
  
}
 
All the operations will create a BetResource for returning to the client. This is done by callingtoResource on the BetResourceAssembler class:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BetResourceAssembler extends ResourceAssemblerSupport {
 
 public BetResourceAssembler() {
  super(BetController.class, BetResource.class);
 }
 
 public BetResource toResource(Bet bet) {
  BetResource resource = instantiateResource(bet);
  resource.bet = bet;
        resource.add(linkTo(BetController.class).slash(bet.getId()).withSelfRel());
  return resource;
 }
 
}
This class extends ResourceAssemblerSupport which requires the implementation of a toResourcemethod as it implements the ResourceAssembler interface. This is where the mapping between Betand BetResource is done. In this case, BetResource is just a wrapper for Bet so it is simply a case of setting the bet attribute. The instantiateResource method will return a BetResource without any links so links can be added at this point if required. In this example a link to self is added. An alternative approach would be to use createResourceWithId which will return a BetResource with the self link. 
 
?
1
2
3
4
5
public class BetResource extends ResourceSupport {
  
 public Bet bet;
  
}
 
Also in this example, links are added to the BetResource within the BetController class to ensure the application of the HATEOAS constraint. If the REST service receives a GET request then a check is made on the status of the Bet. If the Bet is UNMATCHED, then a link to cancel the Betcan be added to the BetResource. This is done in similar fashion to the self link but with the relationship attribute name of cancel.
 
An alternative approach to this is to build a link to a method as opposed to constructing a URI. 
 
?
1
2
resource.add(linkTo(methodOn(BetController.class).cancelBet(betId))
.withRel("cancel"));
 
The methodOn would create a proxy of the BetController class and as a result the return type of thecancelBet method would have to be capable of proxying. Therefore in this example the return type of cancelBet method would be HttpEntity and not ResponseEntity. If the latter, then the likely exception from the server would be:
 
[org.springframework.http.ResponseEntitycom.city81.hateoas.rest.BetResource> com.city81.hateoas.controller.BetController.getBet(java.lang.Long) throws com.city81.hateoas.BetNotFoundException]:org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class org.springframework.http.ResponseEntity]: common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
 
Back to the GET request, and the returned JSON for requesting a Bet resource which has a status of UNMATCHED is shown below:

{
 "links":[
     {"rel":"self","href":http://localhost:8080/hateoas-1-SNAPSHOT/bets/0},
     {"rel":"cancel","href":http://localhost:8080/hateoas-1-SNAPSHOT/bets/0
],
 "bet":{"id":0,"marketId":1,"selectionId":22,"price":4.0,"stake":2.0,"type":"BACK","status":"UNMATCHED"}


The client can therefore use the self link for retrieving and updating the Bet, and also the cancel link to effectively delete it.

This post describes just some of the functionality of the Spring-HATEOAS project which is evolving all the time. For an up to date and more detailed explanation, visit the GitHub pages.