SKM IT World

Just another blog about IT


Leave a comment

Strategy Pattern Revisited With Spring

This blog post wants to show another approach how to implement the Strategy Pattern with dependency injection. As DI framework, I choose Spring framework

From Wikipedia

Firstly, let’s have a look how the Strategy Pattern is implemented in the classic way.
As starting point, we have a HeroController that should add a hero in HeroRepository depends on which repository was chosen by the user.

package com.github.sparsick.springbootexample.hero.universum;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class HeroControllerClassicWay {

    @PostMapping("/hero/new")
    public String addNewHero(@ModelAttribute("newHero") NewHeroModel newHeroModel) {
        HeroRepository heroRepository = findHeroRepository(newHeroModel.getRepository());
        heroRepository.addHero(newHeroModel.getHero());
        return "redirect:/hero";
    }

    private HeroRepository findHeroRepository(String repositoryName) {
        if (repositoryName.equals("Unique")) {
            return new UniqueHeroRepository();
        }

        if(repositoryName.equals(("Duplicate")){
            return new DuplicateHeroRepository();
        }

        throw new IllegalArgumentException(String.format("Find no repository for given repository name [%s]", repositoryName));
    }
}
package com.github.sparsick.springbootexample.hero.universum;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import org.springframework.stereotype.Repository;

@Repository
public class UniqueHeroRepository implements HeroRepository {

    private Set<Hero> heroes = new HashSet<>();

    @Override
    public String getName() {
        return "Unique";
    }

    @Override
    public void addHero(Hero hero) {
        heroes.add(hero);
    }

    @Override
    public Collection<Hero> allHeros() {
        return new HashSet<>(heroes);
    }

}
package com.github.sparsick.springbootexample.hero.universum;

import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@Repository
public class DuplicateHeroRepository implements HeroRepository {

    private List<Hero> heroes = new ArrayList<>();

    @Override
    public void addHero(Hero hero) {
        heroes.add(hero);
    }

    @Override
    public Collection<Hero> allHeros() {
        return List.copyOf(heroes);
    }

    @Override
    public String getName() {
        return "Duplicate";
    }
}

This implementation has some pitfalls. The creation of the repository implementations aren’t managed by the Spring Context (it breaks the dependency injection / inverse of control). This will be painful as soon as you want to expand the repository implementation with further feature that need to inject other classes (for example, counting the usage of this class with MeterRegistry).

package com.github.sparsick.springbootexample.hero.universum;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.stereotype.Repository;

@Repository
public class UniqueHeroRepository implements HeroRepository {

    private Set<Hero> heroes = new HashSet<>();
    private Counter addCounter;

    public UniqueHeroRepository(MeterRegistry meterRegistry) {
        addCounter = meterRegistry.counter("hero.repository.unique");
    }

    @Override
    public String getName() {
        return "Unique";
    }

    @Override
    public void addHero(Hero hero) {
        addCounter.increment();
        heroes.add(hero);
    }

    @Override
    public Collection<Hero> allHeros() {
        return new HashSet<>(heroes);
    }

}

It breaks also the separation of concern. When I want to test the controller class, I have no possibility to mock the repository interface easily. So the first idea is to put the creation of repository implementation to the Spring context. The repository implementation are annotated with @Repository annotation. So Spring’s component scan find them.
The next question how to inject them into the controller class. Here, a Spring feature can help. I define a list of HeroRepository in the controller. This list has to be filled during the creation of the controller instance.

package com.github.sparsick.springbootexample.hero.universum;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import java.util.List;

@Controller
public class HeroControllerRefactoringStep1 {

    private List<HeroRepository> heroRepositories;

    public HeroControllerRefactoringStep1(List<HeroRepository> heroRepositories) {
        this.heroRepositories = heroRepositories;
    }

    @PostMapping("/hero/new")
    public String addNewHero(@ModelAttribute("newHero") NewHeroModel newHeroModel) {
        HeroRepository heroRepository = findHeroRepository(newHeroModel.getRepository());
        heroRepository.addHero(newHeroModel.getHero());
        return "redirect:/hero";
    }

    private HeroRepository findHeroRepository(String repositoryName) {
        return heroRepositories.stream()
                .filter(heroRepository -> heroRepository.getName().equals(repositoryName))
                .findFirst()
                .orElseThrow(()-> new IllegalArgumentException(String.format("Find no repository for given repository name [%s]", repositoryName)));

    }
}

Spring searches in its context for all implementation of the interface HeroRepostiory and put them all to the list. One disadvantage has this solution, every adding a hero browses the list of HeroRepository to find the right implementation. This can be optimized by creating a map in the controller constructor that has the repository name as key and the corresponded implementation as value.

package com.github.sparsick.springbootexample.hero.universum;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
public class HeroControllerRefactoringStep2 {

    private Map<String, HeroRepository> heroRepositories;

    public HeroControllerRefactoringStep2(List<HeroRepository> heroRepositories) {
        this.heroRepositories = heroRepositoryStrategies(heroRepositories);
    }

    private Map<String, HeroRepository> heroRepositoryStrategies(List<HeroRepository> heroRepositories){
        Map<String, HeroRepository> heroRepositoryStrategies = new HashMap<>();
        heroRepositories.forEach(heroRepository -> heroRepositoryStrategies.put(heroRepository.getName(), heroRepository));
        return heroRepositoryStrategies;
    }

    @PostMapping("/hero/new")
    public String addNewHero(@ModelAttribute("newHero") NewHeroModel newHeroModel) {
        HeroRepository heroRepository = findHeroRepository(newHeroModel.getRepository());
        heroRepository.addHero(newHeroModel.getHero());
        return "redirect:/hero";
    }

    private HeroRepository findHeroRepository(String repositoryName) {
        HeroRepository heroRepository = heroRepositories.get(repositoryName);
        if(heroRepository != null) {
            return  heroRepository;
        }
        throw new IllegalArgumentException(String.format("Find no repository for given repository name [%s]", repositoryName));
    }
}

The final question is what if other classes in the application need the possibility to choose a repository implementation during the runtime. I could copy and paste the private method in each class that have this need or I move the creation of the map to the Spring Context and inject the Map to each class.

package com.github.sparsick.springbootexample.hero;

import com.github.sparsick.springbootexample.hero.universum.HeroRepository;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@SpringBootApplication
public class HeroApplicationRefactoringStep3 {

	public static void main(String[] args) {
		SpringApplication.run(HeroApplication.class, args);
	}

	@Bean
    Map<String, HeroRepository> heroRepositoryStrategy(List<HeroRepository> heroRepositories){
        Map<String, HeroRepository> heroRepositoryStrategy = new HashMap<>();
        heroRepositories.forEach(heroRepository -> heroRepositoryStrategy.put(heroRepository.getName(), heroRepository));
        return heroRepositoryStrategy;
    }
}

package com.github.sparsick.springbootexample.hero.universum;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import java.util.Map;

@Controller
public class HeroControllerRefactoringStep3 {

    private Map<String, HeroRepository> heroRepositoryStrategy;

    public HeroControllerRefactoringStep3(Map<String, HeroRepository> heroRepositoryStrategy) {
        this.heroRepositoryStrategy = heroRepositoryStrategy;
    }

    @PostMapping("/hero/new")
    public String addNewHero(@ModelAttribute("newHero") NewHeroModel newHeroModel) {
        HeroRepository heroRepository = findHeroRepository(newHeroModel.getRepository());
        heroRepository.addHero(newHeroModel.getHero());
        return "redirect:/hero";
    }

    private HeroRepository findHeroRepository(String repositoryName) {
        return heroRepositoryStrategy.get(repositoryName);
    }

}

This solution is a little bit ugly, because it isn’t obvious that the Strategy Pattern is used. So the next refactoring step is moving the map of hero repositories to an own component class. Therefore, the bean definition heroRepositoryStrategy in the application configuration can be removed.

package com.github.sparsick.springbootexample.hero.universum;

import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

@Component
public class HeroRepositoryStrategy {

    private Map<String, HeroRepository> heroRepositoryStrategies;

    public HeroRepositoryStrategy(Set<HeroRepository> heroRepositories) {
        heroRepositoryStrategies = createStrategies(heroRepositories);
    }

    HeroRepository findHeroRepository(String repositoryName) {
        return heroRepositoryStrategies.get(repositoryName);
    }

    Set<String> findAllHeroRepositoryStrategyNames () {
        return heroRepositoryStrategies.keySet();
    }

    Collection<HeroRepository> findAllHeroRepositories(){
        return heroRepositoryStrategies.values();
    }


    private Map<String, HeroRepository> createStrategies(Set<HeroRepository> heroRepositories){
        Map<String, HeroRepository> heroRepositoryStrategies = new HashMap<>();
        heroRepositories.forEach(heroRepository -> heroRepositoryStrategies.put(heroRepository.getName(), heroRepository));
        return heroRepositoryStrategies;
    }

}

package com.github.sparsick.springbootexample.hero.universum;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;

import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Controller
public class HeroController {

    private HeroRepositoryStrategy heroRepositoryStrategy;

    public HeroController(HeroRepositoryStrategy heroRepositoryStrategy) {
        this.heroRepositoryStrategy = heroRepositoryStrategy;
    }

    @PostMapping("/hero/new")
    public String addNewHero(@ModelAttribute("newHero") NewHeroModel newHeroModel) {
        HeroRepository heroRepository = heroRepositoryStrategy.findHeroRepository(newHeroModel.getRepository());
        heroRepository.addHero(newHeroModel.getHero());
        return "redirect:/hero";
    }
}

The whole sample is hosted on GitHub.


Leave a comment

Configuration over JNDI in Spring Framework

From a certain point on, an application has to be configurable.  Spring Framework has a nice auxiliary tool for this issue since the first version 0.9 , the class PropertyPlaceholderConfigurer and since Spring Framework 3.1 the class PropertySourcesPlaceholderConfigurer. When you start a Google search for PropertyPlaceholderConfigurer, you will find many examples where the configuration items are saved in properties files. But in many Java enterprise applications, it is common that the configuration items are loaded over JNDI look ups. I’d like to demonstrate how the PropertyPlaceholderConfigurer (before Spring Framework 3.1) and accordingly PropertySourcesPlaceholderConfigurer (since Spring Framework 3.1) can help to ease the configuration over JNDI look ups in our application.

Initial Situation

We have an web application that has a connection to a database. This database connection has to be configurable. The configuration items are defined in a web application context file.


<Context docBase="/opt/tomcat/warfiles/jndi-sample-war.war" antiResourceLocking="true">
  <Environment name="username" value="demo" type="java.lang.String" override="false"/>
  <Environment name="password" value="demo" type="java.lang.String" override="false"/>
  <Environment name="url" value="jdbc:mysql://192.168.56.101:3306/wicket_demo" type="java.lang.String" override="false"/>
</Context> 

For loading these configuration items, the JNDI look up mechanism is used.

In our application we define a data source bean in a  Spring context XML file. This bean represents the database connection.

<?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"
    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.xsd">

  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="url" value="${url}" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
  <bean> 
</beans> 

Every value that starts and ends with ${} should be replaced by PropertyPlaceholderConfigurer and accordingly PropertySourcesPlaceholderConfigurer at the time when launching the application. The next step is to set up PropertyPlaceholderConfigurer and accordingly PropertySourcesPlaceholderConfigurer.

Before Spring Framework 3.1 – PropertyPlaceholderConfigurer Set Up for JNDI Look Up

We define a PropertyPlaceholderConfigurer  bean  in a Spring context XML file. This bean contains to an inner bean that maps the property names of the data source bean to the corresponding JNDI name. The JNDI name consists of two parts. The first part is the name of the context in which the resource is (in our case java:comp/env/) and the second part is the name of the resource (in our case either username, password or url).

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="properties">
        <bean class="java.util.Properties">
            <constructor-arg>
                <map>
                    <entry key="username">
                        <jee:jndi-lookup jndi-name="java:comp/env/username" />
                    </entry>
                    <entry key="password">
                        <jee:jndi-lookup jndi-name="java:comp/env/password" />
                    </entry>
                    <entry key="url">
                        <jee:jndi-lookup jndi-name="java:comp/env/url" />
                    </entry>
                </map>
            </constructor-arg>
        </bean>
    </property>
</bean>

Since Spring Framework 3.1 – PropertySourcesPlaceholderConfigurer Set Up for JNDI Look Up

Since Spring 3.1 PropertySourcesPlaceholderConfigurer should be used instead of PropertyPlaceholderConfigurer. This effects that since Spring 3.1 the <context:property-placeholder/> namespace element registers an instance of PropertySourcesPlaceholderConfigurer (the namespace definition must be spring-context-3.1.xsd) instead of PropertyPlaceholderConfigurer (you can simulate the old behaviour when you use the namespace definition spring-context-3.0.xsd). So our Spring XML context configuration is very short, when you comply some convention (based on the principle Convention over Configuration).

<context:property-placeholder/>

The default  behavior is that the PropertySourcesPlaceholderConfigurer iterates through a set of PropertySource to collect all properties values. This set contains JndiPropertySource per default in a Spring based web application. By default, JndiPropertySource looks up after JNDI resource names prefixed with java:comp/env. This means if your property is ${url}, the corresponding JNDI resource name has to be java:comp/env/url.

The source code of the sample  web application is hosted on GitHub.


5 Comments

Spring Web Application With Hessian Services As a Maven Project

This post describes how to set up a Maven project for a Spring web application with Hessian Service. It also shows how to set up the deployment for exposing the Hessian service and how to set up a client to consume the Hessian service. The Spring Framework version is 3.1.1.RELEASE and the Hessian version is 4.0.7.

Maven Set Up For Server

Our Maven project has three modules

  • hello-world-api
  • hello-world-impl
  • hello-word-war

<modules>
  <module>hello-world-api</module>
  <module>hello-world-impl</module>
  <module>hello-world-war</module>
</modules>

The module hello-world-api contains the interfaces of the services that Hessian server and Hessian client need for the communication. The module hello-world-impl contains the implementation of the services that are deployed on the server side. The module hello-world-war contains the configuration for the servlet container and the configuration which services should be exported as a  Hessian service.

The Service Interface Definition

The module hello-world-api should become a JAR artifact so the packaging jar for this Maven module:


<?xml version="1.0" encoding="UTF-8"?>
<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">

  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.github.skosmalla.spring.hessian</groupId>
    <artifactId>hello-world-spring-hessian</artifactId>
    <version>1.0.0-SNAPSHOT</version>
  </parent>

  <artifactId>hello-world-api</artifactId>

  <name>Hello World Api</name>

</project>

The jar contains the interfaces of the services. An example


package com.github.skosmalla.hello.world.spring.hessian;

public interface HelloWorld {

  public String welcome();

}

The Service Implementation

The module hello-world-impl also becomes a JAR artifact. This jar contains the service implementation for the server. The service implementation could look like following code:


package com.github.skosmalla.hello.world.spring.hessian;

public class HelloWorldImpl implements HelloWorld {

  @Override
  public String welcome() {
    return "Hello World";
  }

}

Thereby this implementation can be used as a Hessian service, it has to be defined as a Spring bean. Therefore we need a Spring configuration file hello-world-service-config.xml. The location for this file is src/main/resources/META-INF/spring. The bean configuration looks like the following code:


<?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="helloWorldService" class="com.github.skosmalla.hello.world.spring.hessian.HelloWorldImpl"/>

</beans>

In this module we have two dependencies, one to the API module and one to the spring-beans module.


<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
  </dependency>
  <dependency>
    <groupId>com.github.skosmalla.spring.hessian</groupId>
    <artifactId>hello-world-api</artifactId>
  </dependency>
</dependencies>

The WAR Deployment

The module hello-world-war  describes the configuration for the server deployment. The artifact of this module becomes a WAR file. Therefore, the packaging of this Maven module is war.


<?xml version="1.0" encoding="UTF-8"?>
<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">

  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.github.skosmalla.spring.hessian</groupId>
    <artifactId>hello-world-spring-hessian</artifactId>
    <version>1.0.0-SNAPSHOT</version>
  </parent>

  <artifactId>hello-world-war</artifactId>

  <name>Hello World WAR </name>

  <packaging>war</packaging>

  <build>
    <defaultGoal>install</defaultGoal>
  </build>
</project>

Now, we have to do two things for running our server application on a servlet container:

  1. Add configuration of Spring application context with our implementation to the servlet container.
  2. Add configuration to dispatch request to our Hessian service.

To create an ApplicationContext instance in a web application, we have to configure an ContextLoaderListener in our Web Application Deployment Descriptor (location: src/main/webapp/WEB-INF/web.xml):

<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:META-INF/spring/*.xml</param-value>
</context-param>

<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

The ContextLoaderListener builds the root application context from all Spring configuration files located in the classpath under META-INF/spring (That pattern matches our hello-world-service-config.xml).  But no request can be processed. Therefore we have to configure a servlet that dispatch the request to the service. Here, the Spring Framework supports us with a DispatcherServlet. To use it we have to add that servlet to our Web Application Deployment Descriptor (location: src/main/webapp/WEB-INF/web.xml):

<servlet>
  <servlet-name>hessian</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
  <servlet-name>hessian</servlet-name>
  <url-pattern>/hessian/*</url-pattern>
</servlet-mapping>

That configuration means that servlet is named as hessian and it is responsible for all request to the URL http://<URL to the Tomcat instanz>/<webapp-context>/hessian/* . Now, we have to configure the Hessian service interface that are dispatched by the servlet hessian. For it, we have to add a Spring configuration file in the same location like Web Application Deployment Descriptor (src/main/webapp/WEB-INF). The name of that file have to be hessian-servlet.xml (the pattern is <servlet name>-servlet.xml). Here, we configure the Hessian service interface:


<bean name="/HelloWorldService" class="org.springframework.remoting.caucho.HessianServiceExporter">
  <property name="service" ref="helloWorldService" />
  <property name="serviceInterface" value="com.github.skosmalla.hello.world.spring.hessian.HelloWorld" />
</bean>

That Spring configuration file defines a new application context. It is child application context of the root application context, loaded by the ContextLoaderListener. A child application context can see every bean of the root application context, but the root application context cannot see beans of its child application context (for more information, have look at the Spring Framework reference). The HessianServiceExporter has a reference to the service implementation, defined in the root application context.
The URL of that Hessian service interface is http://<URL to the Tomcat instanz>/<webapp-context>/hessian/HelloWorldService (Pattern is http://<URL to the Tomcat instanz>/<webapp-context>/hessian/<bean name of the HessianServiceExporter>).

So that these configurations can run in a servlet container, we have to add the dependencies, that contains HessianServiceExporter, DispatcherServlet and ContextLoaderListener, to the pom.xml :


<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
  <groupId>com.github.skosmalla.spring.hessian</groupId>
  <artifactId>hello-world-impl</artifactId>
</dependency>
<dependency>
  <groupId>com.caucho</groupId>
  <artifactId>hessian</artifactId>
</dependency>

The Hessian dependency is needed at runtime and the hello-world-impl contains our business logic and the spring configuration file for the root application context.

With a mvn clean install Maven builds a WAR file in the project’s target folder. This WAR file can be deployed on a Tomcat.

The Hessian Test Client With Spring

Now we write a client to test the Hessian service. Therefore, we create a new Maven module.


<?xml version="1.0" encoding="UTF-8"?>
<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">

  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.github.skosmalla.spring.hessian</groupId>
    <artifactId>hello-world-spring-hessian</artifactId>
    <version>1.0.0-SNAPSHOT</version>
  </parent>

  <artifactId>hello-world-client</artifactId>

  <name>Hello World Client</name>

</project>

Spring Framework offers a HessianProxyFactoryBean for calling the remote HelloWorld service. The configuration for this HessianProxyFactoryBean could look like the following code snippet:


<?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="helloWorldService"
  class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
  <property name="serviceUrl"
    value="http:/localhost:8080/hello-world/hessian/HelloWorldService" />
  <property name="serviceInterface"
    value="com.github.skosmalla.hello.world.spring.hessian.HelloWorld" />
</bean>
</beans>

In the property serviceInterface we define the interface of the Hessian service, here com.github.skosmalla.hello.world.spring.hessian.HelloWorld. In the property serviceUrl we define the URL to the Hessian Service deployed on the Tomcat. In our sample the Tomcat is on localhost with port number 8080 and the web application is hello-world.

Now, this factory bean creates Hessian service proxy for us:


package com.github.skosmalla.hello.world.spring.hessian;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class HessianClient {

  public static void main(String[] args) {
    ApplicationContext appContext = new ClassPathXmlApplicationContext("META-INF/spring/hessian-config.xml");

    HelloWorld service = (HelloWorld) appContext.getBean("helloWorldService");

    String welcomeMessage = service.welcome();

    System.out.println(welcomeMessage);

  }
}

The dependencies for the client are the following one:

<dependencies>
  <dependency>
    <groupId>com.github.skosmalla.spring.hessian</groupId>
    <artifactId>hello-world-api</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
  </dependency>
  <dependency>
    <groupId>com.caucho</groupId>
    <artifactId>hessian</artifactId>
    <scope>runtime</scope>
  </dependency>
</dependencies>

If we start this client we will get “Hello World” on our command line.

Now, we have seen a full example how to set up a Hessian service in a Spring web application and how to call such a service remotely. The full code you can find on Github.

Links


Leave a comment

Read Classpath Resource from Jar Files with Spring

To read resources from classpath, you can use the Spring class ClassPathResource. The constructor with one argument reads only resources from file system.  But sometimes you want to read a resources from a jar file in the classpath.  For this case, you must build an own URLClassLoader from the URL of the jar file and use the ClassPathResource constructor with the arguments path and classLoader.  The path value is the path of the resource in the jar file.

An Example

We have a jar file, named example.jar. The content looks as follows:

example.jar
|_ META-INF
   |_ sample-resource.txt

We want to read the sample-resource.txt.  The source code for this example looks as follows:

URL jarUrl = new File("path_to_jar_file").toURI().toURL();
URLClassLoader jarLoader = new URLClassLoader(new URL[]{jarUrl}, Thread.currentThread().getContextClassLoader());
ClassPathResource sampleResource = new ClassPathResource("META-INF/sample-resource.txt",jarLoader);