Skip to main content

Changing source code at run-time with Service Locator pattern

I have a service to get some data but the result can be different basing on where the implementation is. Technically, I have two or more concrete implementation of an interface and I am able to switch using these concrete classes at run-time. That means I have a place to configure it without re-deploying the application. In order to overcome this issue, I use Service Locator design pattern and here I only care about two advantages below:
  • Encapsulating the specific implementation, we just declare the name and don't care about the implementation of the service.
  • Changing the implementation at run-time.



Client: an object that invokes the services via Service Locator
Business services: services that is used by Client.


Once again, I used the JFS Helloworld example from previous post for this example.

1. Create a service interface "CountryService" and two concrete classes "CountryService1" and "CountryService2" (Business services)

package vn.nvanhuong.servicelocator.service;

import java.util.List;

public interface CountryService{
 public List<String> getCountries();
}

The first concrete service:
package vn.nvanhuong.servicelocator.service.impl;

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

import vn.nvanhuong.servicelocator.service.CountryService;

public class CountryService1 implements CountryService{

 public List<String> getCountries() {
  List<String> result = new ArrayList<String>();
  result.add("Vietname");
  result.add("Switzerland");
  result.add("Japan");
  return result;
 }

}

The second concrete service:
package vn.nvanhuong.servicelocator.service.impl;

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

import vn.nvanhuong.servicelocator.service.CountryService;

public class CountryService2 implements CountryService{

 public List<String> getCountries() {
  List<String> result = new ArrayList<String>();
  result.add("Vietname");
  result.add("America");
  result.add("China");
  return result;
 }

}

2. Create class "InitialContext" that is used for looking up and creating classes basing on the provided names
package vn.nvanhuong.servicelocator;

import java.lang.reflect.Constructor;

public class IntitialContext {
 
 public Object lookup(String serviceName){
    
  if(serviceName != null){
   try {
    Class<?> clazz = Class.forName(serviceName);
    Constructor<?> ctor = clazz.getConstructor();
    Object object = ctor.newInstance();
    return object;
   } catch (Exception ex) {
    ex.printStackTrace();
   } 
  }
  return null;
 }

}


3. Create class "ServiceLocator", I used Singleton to cache the object.
package vn.nvanhuong.servicelocator;

public class ServiceLocator {
 private static ServiceLocator instance;
 
 private ServiceLocator(){}
 
 public static synchronized ServiceLocator getInstance(){
  if(instance == null){
   return new ServiceLocator();
  }
  return instance;
 }

 public Object getService(String serviceName) {
  IntitialContext initialContext = new IntitialContext();
  return initialContext.lookup(serviceName);
 }

}

4. Create a resource file "services.properties" in order to configure the changing implementation at run-time. :)
country = vn.nvanhuong.servicelocator.service.impl.CountryService1
language = vn.nvanhuong.servicelocator.service.impl.LanguageService1

5. Calling SeviceLocator in Managed bean (Client)
package vn.nvanhuong.servicelocator.bean;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;

import javax.faces.bean.ManagedBean;

import vn.nvanhuong.servicelocator.ServiceLocator;
import vn.nvanhuong.servicelocator.service.CountryService;

@ManagedBean(name = "helloBean")
public class HelloBean {
 private List<String> countries;
 
 public HelloBean() throws IOException {
  Properties prop = new Properties();
     InputStream input = null;
     
     String filename = "services.properties";
  input = HelloBean.class.getClassLoader().getResourceAsStream(filename);

  prop.load(input);
  CountryService countryService = (CountryService) ServiceLocator.getInstance()
              .getService(prop.getProperty("country"));
        this.countries = countryService.getCountries();
  
 }

 public List<String> getCountries() {
  return countries;
 }

 public void setCountries(List<String> countries) {
  this.countries = countries;
 }
 
}
6. GUI code: index.xhtml
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
 xmlns:f="http://java.sun.com/jsf/core"
 xmlns:h="http://java.sun.com/jsf/html"
 xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
 <title>Service Locator</title>
</h:head>
<h:body>
 <h3>Lis of countries:</h3>
 <h:dataTable value="#{helloBean.countries}" var="country">
   <h:column>
         <h:outputText value="#{country}" />
     </h:column>
 </h:dataTable>

</h:body>
</html>

7. Test (on Tomcat v7.0)

http://localhost:8080/service_locator/

Use the "CountryService1" by changing in "services.properties", don't need to restart the server.

country = vn.nvanhuong.servicelocator.service.impl.CountryService1




Use the "CountryService2" by changing in "services.properties", don't need to restart the server.

country = vn.nvanhuong.servicelocator.service.impl.CountryService2

Here we also can add more concrete classes of CountryService and just declare in file "services.properties" for using. For this reason, I use Java Reflection to create the objects basing on the provided names in general way otherwise we have to change the InitialContext whenever we want to add new concrete class of "CountryService".

Note:
There might have several ways to implement this pattern but the idea is general so that my example is only a case. We also can improve my implementation by using caching technique, see reference [3].


Reference:

Comments

Popular posts from this blog

JSF 2 - Dynamically manipulating the component tree with system events

Let's suppose we want to modify the metadata (attributes)  of elements such as render , requried , maxlength but we do not define in JSF tags. The manipulating components can be conducted in Drools  files, for example. How could we do? I think that is what we need to change something of component tree during JSF life-cycle. JSF supports event handling throughout the JSF life-cycle. In this post, I use two events: postAddToView for scanning components tree and preRenderView for manipulating the meta of components before rendering to GUI. I modified my own project from previous post for this example. This is my first further JSF trying out with the project as I said before. :) We define the tags f:event below the form - a container component of the components which we want to work on. The valid values for the attribute type for f:event can be found from tag library document  of JSF 2. <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" x...

Attribute 'for' of label component with id xxxx is not defined

I got the warning in the log file when I have used the tag <h:outputLabel> without attribute " for " in xhtml file. It was really polluting my server log files. The logged information actually makes sense anyway! We could find an answer as the following: "Having h:outputLabel without a "for" attribute is meaningless. If you are not attaching the label, you should be using h:outputText instead of h:outputLabel." However, these solutions are not possible just for my situation. Instead of using h:outputText for only displaying text, my team has used h:outputLabel too many places. We were nearly in our release time (next day) so it is quite risky and takes much efforts if we try to correct it. Because the style (with CSS) is already done with h:ouputLabel . The alternative by adding attribute " for " the existing h:outputLabel is not reasonable either. I really need to find another solution. Fortunately, I came across a way if I cha...

Building Axon.ivy Projects on Bitbucket Pipelines

Read me  if you don't know what Axon.ivy (Ivy) is. Motivation -  Ivy projects are designed to be built on a continuous integration (CI) server like Jenkins - Today, Bitbucket supports for CI with Bitbucket Pipelines - We're using Bitbucket. Then, why not? It must be very cool and convenient for us if we can centralize our CI and VCS (version control system) tools in one place. Here is an approach We have to use a maven plugin called project-build-plugin  to build ivy projects. This plugin requires an instance of Ivy engine during building time. Bitbucket Pipelines allows us to specify our own docker image as a build environment. What we need to do  is to prepare our docker image with needed stuffs such as JDK, Maven, Ivy engine, etc. Step 1. Prepare Docker images For testing purpose, I already created two docker images: Maven and Axon.ivy engine. They are now available on Docker Hub This image for Maven using Oracle JDK 8 This image for Axon.iv...

Junit - Test fails on French or German string assertion

In my previous post about building a regex to check a text without special characters but allow German and French . I met a problem that the unit test works fine on my machine using Eclipse, but it was fail when running on Jenkins' build job. Here is my test: @Test public void shouldAllowFrenchAndGermanCharacters(){ String source = "ÄäÖöÜüß áÁàÀâÂéÉèÈêÊîÎçÇ"; assertFalse(SpecialCharactersUtils.isExistSpecialCharater(source)); } Production code: public static boolean isExistNotAllowedCharacters(String source){ Pattern regex = Pattern.compile("^[a-zA-Z_0-9_ÄäÖöÜüß áÁàÀâÂéÉèÈêÊîÎçÇ]*$"); Matcher matcher = regex.matcher(source); return !matcher.matches(); } The result likes the following: Failed tests: SpecialCharactersUtilsTest.shouldAllowFrenchAndGermanCharacters:32 null A guy from stackoverflow.com says: "This is probably due to the default encoding used for your Java source files. The ö in the string literal in the J...

Styling Sort Icons Using Font Awesome for Primefaces' Data Table

So far, Primefaces has used image sprites for displaying the sort icons. This leads to a problem if we want to make a different style for these icons; for example, I would make the icon "arrow up" more blurry at the first time the table loading because I want to highlight the icon "arrow down". I found a way that I can replace these icons with Font Awesome icons. We will use "CSS Pseudo-classes" to achieve it. The hardest thing here is that we should handle displaying icons in different cases. There is a case both "arrow up" and "arrow down" showing and other case is only one of these icons is shown. .ui-sortable-column-icon.ui-icon.ui-icon-carat-2-n-s { background-image: none; margin-left: 5px; font-size: 1.1666em; position: relative; } .ui-sortable-column-icon.ui-icon.ui-icon-carat-2-n-s:not(.ui-icon-triangle-1-s)::before { content: "\f106"; font-family: "FontAwesome"; position: ...