vendredi 17 janvier 2014

Unit tests and integration tests coverage in sonar with gradle

I recently needed to configure a multi-module gradle project in order to generate unit test, integration test and overall coverage indicators in sonar. As it wasn't so obvious I thought it would be a good idea to share my example.

The goal is to generate the following report in sonar :


Complete gradle file


So here is my complete build.gradle file :

apply plugin: 'sonar-runner'

allprojects {
  apply plugin: 'java'
  apply plugin: 'jacoco'

  sourceSets {
    integtest {
      java {
        compileClasspath += main.output
        runtimeClasspath += main.output
        srcDir 'src/integtest/java'
      }
    }
  }

  configurations {
    integtestCompile.extendsFrom testCompile
  }

  task "integtest"(type: Test, dependsOn: integtestClasses) {
    testClassesDir = sourceSets.integtest.output.classesDir
    classpath = sourceSets.integtest.runtimeClasspath

      jacoco {
          destinationFile = file("$buildDir/jacoco/jacocoIntegTest.exec")
      }
  }

  test {
    jacoco {
      destinationFile = file("$buildDir/jacoco/jacocoTest.exec")
    }
  }

  sonarRunner {
    sonarProperties {
      property "sonar.jacoco.reportPath", "$buildDir/jacoco/jacocoTest.exec"
      property "sonar.jacoco.itReportPath", "$buildDir/jacoco/jacocoIntegTest.exec"
    }
  }
}

sonarRunner {
    sonarProperties {
        property "sonar.projectName", "test-samples"
        property "sonar.projectKey", "jsebfranck.samples"
    }
}

Explanations

I used Jacoco to generate my tests coverage indicators :

  apply plugin: 'jacoco'


The following lines allow to configure my integration tests. To distinguish these tests from the unit tests, I moved them in a specific source folder : "src/integtest/java". Please note that with gradle, the default test folder is "src/test/java" and I use it for my unit tests.

  sourceSets {
    integtest {
      java {
        compileClasspath += main.output
        runtimeClasspath += main.output
        srcDir 'src/integtest/java'
      }
    }
  }

  configurations {
    integtestCompile.extendsFrom testCompile
  }

  task "integtest"(type: Test, dependsOn: integtestClasses) {
    testClassesDir = sourceSets.integtest.output.classesDir
    classpath = sourceSets.integtest.runtimeClasspath
  }

I configured Jacoco to generate the tests coverage data in a specific directory : one for the unit tests and for the integration tests :

  task "integtest"(type: Test, dependsOn: integtestClasses) {
    testClassesDir = sourceSets.integtest.output.classesDir
    classpath = sourceSets.integtest.runtimeClasspath

      jacoco {
          destinationFile = file("$buildDir/jacoco/jacocoIntegTest.exec")
      }
  }

  test {
    jacoco {
      destinationFile = file("$buildDir/jacoco/jacocoTest.exec")
    }
  }

Then for each module, I configured sonar to use generated Jacoco data :
  • reportPath property allows to configure the unit tests coverage
  • itReportPath property allows to configure the integration tests coverage
allprojects {
  sonarRunner {
    sonarProperties {
      property "sonar.jacoco.reportPath", "$buildDir/jacoco/jacocoTest.exec"
      property "sonar.jacoco.itReportPath", "$buildDir/jacoco/jacocoIntegTest.exec"
    }
  }
}

Finally I configured my sonar instance. Here I use the default parameters so the report is generated on my localhost sonar installation and its h2 database :

sonarRunner {
    sonarProperties {
        property "sonar.projectName", "test-samples"
        property "sonar.projectKey", "jsebfranck.samples"
    }
}


Source code

Please find the complete source code on my github repository about tests.



dimanche 5 janvier 2014

Mockito in the trenches

I used EasyMock framework for three years, before starting to use Mockito two years ago and I'm now really happy with this framework.

However, it provides some features I never used, and for some features it provides several ways to write your tests. For example you have two ways to create your mocks and two ways to write your expectations.

So, in this article I will present every feature I use, and how I choose to write my Mockito tests.

Our example

Let's start with a simple example, then we will go deeper into the framework.

The class we want to test is a class managing accounts, AccountService, with a single dependency, its repository : AccountRepository. For the moment, AccountService has only one method :

public class AccountService {
  @Inject
  private AccountRepository accountRepository;

  public Account getAccountByLogin(String login) {
    try {
      return accountRepository.findAccount(login);
    } catch (EntityNotFoundException enfe) {
      return null;
    }
  }
}

How can we test that with Mockito?

Mocks creation


The first thing you have to do is to create your class of test and create your mocks. I recommend to use the Mockito annotations and the Junit runner as it is more elegant and concise :

@RunWith(MockitoJUnitRunner.class)
public class AccountServiceTest {
  @InjectMocks
  private AccountService accountService;

  @Mock
  private AccountRepository accountRepository;
}

As you can see, you don't need a @Before method and you don't have to instantiate your objects. All is done automatically by the framework. To inject the mocks in the class to test, Mockito tries to do that using firstly the object constructor, then the object setters or finally the object properties.

doReturn and verify


Now let's test the method getAccountByLogin and the case where an account already exists.

  private static final String LOGIN = "login";
  private static final String PASSWORD = "password";

  @Test
  public void getAccountByLogin_withExistingAccount_shouldReturnTheAccount() 
    throws Exception {
    // Given
    Account existingAccount = new Account(LOGIN, PASSWORD);
    doReturn(existingAccount).when(accountRepository).findAccount(LOGIN);

    // When
    Account result = accountService.getAccountByLogin(LOGIN);

    // Then
    assertEquals(existingAccount, result);
    verify(accountRepository).findAccount(LOGIN);
    verifyNoMoreInteractions(accountRepository);
  }

Explanations :

  • doReturn allows to override the behavior of the AccountRepository.findAccount method. Here we return an Account object to simulate the case that an account already exists
  • verify(accountRepository).findAccount allows to check that this method has been called
  • verifyNoMoreInteractions method allows to be sure that we didn't call another method on this mock. It is useful in that case because you test that you don't do a useless call on your repository

doThrow

Now let's test the case where the account doesn't exist. In that case the repository throws an EntityNotFoundException :

  @Test
  public void getAccountByLogin_withUnexistingAccount_shouldReturnNull() throws Exception {
    // Given
    doThrow(new EntityNotFoundException()).when(accountRepository).findAccount(LOGIN);

    // When
    Account result = accountService.getAccountByLogin(LOGIN);

    // Then
    assertNull(result);
  }

As we used doReturn before, here we use doThrow method. Easy!

doReturn vs thenReturn, doThrow vs thenThrow

With mockito the following instructions are similar :

  // either :
  doReturn(null).when(accountService).getAccountByLogin(LOGIN);
  // or :  
  when(accountService.getAccountByLogin(LOGIN)).thenReturn(null);

  // either :
  doThrow(new EntityNotFoundException()).when(accountRepository).findAccount(LOGIN);
  // or :
  when(accountRepository.findAccount(LOGIN)).thenThrow(new EntityNotFoundException());

However, in the case you want to test a void method, you can write :

  doThrow(new EntityNotFoundException()).when(accountRepository).deleteAccount(LOGIN);

But you cannot write the following instruction because it doesn't compile :

  when(accountRepository.deleteAccount(LOGIN)).thenThrow(new EntityNotFoundException());

So I recommend to always use the do* methods in order to keep your tests coherent.

Object.equals and argument captor


Now let's add another method which create an account :

  public void createAccount(String login, String password) throws ServiceException {
    Account account = new Account(login, password);
    accountRepository.createAccount(account);
  }

It seems very easy to write this test. Let's do a first try :

  @Test
  public void createAccount_nominalCase_shouldCreateTheAccount() throws Exception {
    // When
    accountService.createAccount(LOGIN, PASSWORD);

    // Then
    verify(accountRepository).createAccount(new Account(LOGIN, PASSWORD));   
    verifyNoMoreInteractions(accountRepository);
  }

But unfortunately the test fails! Why? Because the verify instruction checks that the parameters passed in the createAccount method are the same in the test that in the tested code. However, in that case, the Account object has no equals method.

You may think to add an equals method in the Account object but if you do that only for your unit test, it is really a shame. Hopefully Mockito provides a cleaner way to test this case : the argument captor.

  @Test
  public void createAccount_nominalCase_shouldCreateTheAccount() throws Exception {
    // When
    accountService.createAccount(LOGIN, PASSWORD);

    // Then
    assertThatAccountAsBeenCreated();
    verifyNoMoreInteractions(accountRepository);
  }
  
  private void assertThatAccountAsBeenCreated() {
    ArgumentCaptor argument = ArgumentCaptor.forClass(Account.class);
    verify(accountRepository).createAccount(argument.capture());
    Account createdAccount = argument.getValue();

    assertEquals(LOGIN, createdAccount.getLogin());
    assertEquals(PASSWORD, createdAccount.getPassword());    
  }

With this technic, you can retrieve the object passed in the tested code, then do every assertion you want. Here we just want to be sure that the login and the password are correct.

Partial mocking

Now let's add three lines of code in our createAccount method to check that the account doesn't exist before we create it. To do that, we use the existing method getAccountByLogin :

  public void createAccount(String login, String password) throws ServiceException {
    if (getAccountByLogin(login) != null) {
     throw new ServiceException(String.format("The account %s already exists", login));
    }

    Account account = new Account(login, password);
    accountRepository.createAccount(account);
  }

As you have already tested the getAccountByLogin, you don't want to test it again because our tests could become unmaintainable.

So here you can use the partial mocking. The idea is to mock the call to the getAccountByLogin method within the class you are testing. With Mockito (unlike other mock frameworks) it is very easy to do that. You just have to override your method behavior like if you were using a mock :

  @Test
  public void createAccount_nominalCase_shouldCreateTheAccount() throws Exception {
    // Given
    doReturn(null).when(accountService).getAccountByLogin(LOGIN);

    // When
    accountService.createAccount(LOGIN, PASSWORD);

    // Then
    assertThatAccountAsBeenCreated();
    verifyNoMoreInteractions(accountRepository);
    verify(accountService).getAccountByLogin(LOGIN);
  }

Then you just have to add the @Spy annotation on the declaration of your tested object :

  @InjectMocks
  @Spy
  private AccountService accountService;

And it works!

Conclusion

I think I covered every feature of Mockito I use every day. Don't hesitate to ping me if I forgot something important. Anyway, I hope this tutorial helped you to have a better understanding of this great framework!

The whole code, with more cases and tests, is available on my github account.

Also, don't hesitate to consult my recommendations to write maintainable unit tests.

Cheers!


samedi 12 octobre 2013

What is a good code review?

Every programmer does mistake in his code, whatever are his programming skills. You can detect these mistakes at different level in the development workflow : from the development itself to the production. What is sure is that the faster you will find a mistake, the faster you will correct it.

Reread the code, even if it works, is a good way to find potential bugs as fast as possible. This reading is called the code review.

Every team has his own way to do a code review. Some ways are bad, others are better. In this thread, I will present you what is for me the perfect code review, and we will see that a good code review has others benefits that just reveal potential bugs.

How?

The reader and the programmer must read the code together, side by side using the programmer computer. It encourages both of them to discuss and debate about the code in order to challenge it as most as possible.

The programmer must not introduce the modifications to the reader because the reader must understand himself the code. Indeed, the code must be clear enough because other teammates will have to work on it. So the reader must take the mouse and keyboard command.

To be sure to read every modified line of code, the review must be done from the modifications diff of the version control software. Of course, if necessary, the reader can have a look in the entire code.

If the reader see something which must be changed in the code, the programmer must take a note to don't forget to change it after the code review. Every modification must be done just after the code review.

What?

The reader has the responsibility to approve that the code is correct. If he doesn't see a bug during the code review, he will have the same responsibility than the programmer if a problem occurs in production. So every line of code must be checked and understood.

Several kind of problems can be detected in the code and fixed with the code review. For examples, the reader could do the following remarks to the programmer :

  • "The format of these two lines of code doesn't respect our coding conventions"
  • "When I read the method name, I don't understand what is his responsibility"
  • "In this case, you will have a NullPointerException with this variable"
  • "For this line of code, there already exists an utility method in the util package"
  • "A unit test is missing for this case"
  • "Here, you are doing a database access in a loop, you should refactor your code"
  • ...
As you see, the code review allows to be sure that the code will respect the coding conventions, that the code will be understandable, and that potential bugs can be detected.

When?

When the development of a feature is finished, the developer has to demonstrate his work and to ask somebody to read his code.

If the developer starts with the code review and finishes with the demonstration, it is possible that he has to modify his code after the demonstration because the feature doesn't fit all the functional requirements. So a second code review will be necessary.

On the other side, if he starts with the demonstration and finishes with the code review, it is possible that he has to modify his code. But in this case, as the developer directly understood the functional requirements and wrote regression tests, it is not necessary to re-demonstrate the feature.

So the best way is to start to demonstrate the feature and to finish with the code review.

Who?

Who must read the developer code? The technical leader? A senior developer? A junior developer? Depending on the case, every teammate must be able to do a code review. Two parameters must be taken into account to decide who can read who : The first one is the technical level of both the developer and the reader. The second one is the criticality of the feature.

For a critical feature (something complex or really important for the business), the reader must be experimented, so either a senior developer or the technical leader.

For a "basic" feature, it is better to avoid to let a junior developer read another junior developer. Indeed they may don't know every coding conventions or miss bugs or performance issues. But a junior developer can read a senior developer : it is a good way for the junior to improve his competences.

Conclusion : benefits of a good code review

A good code review allows you to fix several bugs and to refactor your code really quickly. You have so the insurance to have a best quality in your software and in your code.

But more than that, encourage your team to read the code allows them to have a best knowledge of the code and to improve their competences very easily.

So if you think that this kind of code review is a waste of time, you are mistaken because you will have less bugs in production, a code of quality, and to finish a more competent team, so a more effective team.

dimanche 29 septembre 2013

Gatling dynamic parameters example

It is very easy to create a stress test with Gatling when you know the parameters to send within each HTTP request. But sometimes you have to handle dynamic parameters and this is more complicated to manage.

A typical case where dynamic parameters are needed is when you are testing a page with a form protected with a CSRF protection, for example :

<form action="/deleteAccount" method="POST">
  <input type="hidden" name="login" value="jsebfranck"/>
  <input type="hidden" name="verificationToken" value="wQvhCLsPHVbKd9ANoOZTpBsXtVhpG0h3"/>
</form>

For each display of this form, the verificationToken parameter expected on the server side will be different. This protection against CSRF attacks allows to be sure that the POST request is send from a form generated by the website, and not from another website.

If you use the gatling recorder to display the form page then submit the form, it will generate the following test :

val scn = scenario("delete account")
      .exec(http("get delete account page")
        .get("/Account/Delete")
        .headers(headers_1))
      .exec(http("delete account")
        .post("/Account/Delete")
        .headers(headers_5)
        .param("verificationToken", "wQvhCLsPHVbKd9ANoOZTpBsXtVhpG0h3")
        .param("login", "jsebfranck"))

The problem with this test is that it always sends the same verificationToken parameter within the form. So if you launch it, it will failed because the verificationToken will be different from the expected verificationToken.

To resolve this problem you have to parse the page with the form in order to retrieve the verificationToken parameter and to send it within the form. To do that you can use two advanced features of gatling, the "session" and the "saving data" features :

val scn = scenario("delete account")
      .exec(http("get delete account page")
        .get("/Account/Delete")
        .headers(headers_1)
        .check(
          regex("""<input name="verificationToken" type="hidden" value="([^"]*)" />""")
          .saveAs("requestVerificationToken")))
      .exec(http("delete account")
        .post("/Account/Delete")
        .headers(headers_5)
        .param("verificationToken", "${requestVerificationToken}")
        .param("login", "jsebfranck"))

The check method allows to do operations on the result of the request. Here we apply a regex to get the content of the value attribute, then we save it in the requestVerificationToken variable with the saveAs method :

    .check(
      regex("""<input name="verificationToken" type="hidden" value="([^"]*)" />""")
      .saveAs("requestVerificationToken")))

Then we can reuse the requestVerificationToken variable to send it within the form :

    .param("verificationToken", "${requestVerificationToken}")

Now you are able to use dynamic parameters in a Gatling test. I hope this example will help you to bypass most of the constraints of a website. For further information on gatling advanced usages, please visit this page.

jeudi 26 septembre 2013

Keep your code simple with YAGNI

One key principle of success for a web site is its capacity to always improve itself with new features and to do it faster than its competitors. To achieve that, the IT team must be able to create features as fast as possible at the different phases of the realization : development, tests and deployment.

During the development phase, one axis to work faster is to always have the more simple, readable and modifiable code. Besides several practices well known like code review or refactoring, there is an important principle which is not very well known by developers : YAGNI. Before explaining this principle, let's see how could a developer work without YAGNI.

Scenario without YAGNI

A agile development team has in its backlog several features and only the three first features are absolutely mandatory before the deployment in production :
  • Creation of a user (must have)
  • User listing (must have)
  • Deletion of a user (must have)
  • Modification of a user (nice to have)
  • Search a user by login (nice to have)
A developer begins to work on the "Creation of a user example" feature. To do that he has to create the user interface, the business logic and the DAO layer.

When he creates the DAO layer, he adds a method "createUser" and then he directly creates several others methods : "getUsers", "deleteUser", "updateUser" and "searchUserByLogin". The developer did that because he is quite sure that these methods will be necessary for the four other features of the backlog.

public interface UserDao {
  public void createUser(User user);
  
  public void updateUser(User user);
  
  public void deleteUser(User user);
  
  public User searchUserByLogin(String login);
  
  public List getUsers();
}

Then specifications change

The three mandatory features are developed and pushed in production. 

Having directly the feedback of the use of these three features in production, the two remaining features specifications change :
  • Modification of a user : it is not necessary anymore to modify a user as it is easy to delete a user and to create a new one
  • Search a user by login by first name : is is finally better to search the user by his first name.

And the developer modifies the code

As it is not necessary anymore to modify a user, the method "updateUser" has been created for nothing. And as the user search must be done by the first name rather than by the user login : the searchUserByLogin method must be changed.

The developer still thinks that the "updateUser" method could be useful in the future, so he doesn't delete it. Moreover, he keeps the "searchUserByLogin" method and create a new method "searchUserByFirstName".

So finally in the DAO layer, we have six methods but only four methods are used :

public interface UserDao {
  public void createUser(User user);
  
  public void updateUser(User user); // useless
  
  public void deleteUser(User user);
  
  public User searchUserByLogin(String login); // useless
  
  public User searchUserByFirstName(String firstName);
  
  public List getUsers();
}

The main mistake the developer did is to add unnecessary code in his program because he was thinking it could be useful in the future. The second mistake he did is to don't clean his code after the specifications change.

Maybe you don't realize why these two methods can be a problem in your code. So try to imagine several developers doing this mistake every day : it will be quickly the mess in your code.

You Ain't Gonna Need It!

Based on the fact that we cannot know or guess the future needs of a software, the idea of YAGNI is to never add code or feature which is not immediately necessary.

More than avoid to have useless methods in the code, the main advantages of the YAGNI principle are :
  • Your code is smaller, so more simple 
  • You don't have to maintain unnecessary code so you win time when you refactore your code 
  • You don't have pieces of code which have never been tested in production and you don't risk to use it thinking it is working

jeudi 29 août 2013

Easy stress tests with Gatling

A few month ago, I needed to compare the response time of several webapp pages before and after upgrading a cache component. I thought the easier and faster way to do that would be to write a shell script doing several http calls to the webapp using wget, and to compute myself the average response time. I did that, it worked and I had my expected conclusion that the new cache version was faster. Of course, I wrote my shell script in a dirty way and it finally finished to the trash.

Then I hear about Gatling, a simple alternative to JMeter to do stress tests. I tested it and indeed it was very easy to use it, and even faster than creating an homemade script.

Today I would like to show you how it is easy to write a stress test with this tool.


Start easily with the recorder



Gatling provides an HTTP recorder tool allowing to generate a Gatling test. It is a good way to generate a first test.

To do that :

1. Download a Gatling archive and extract it
2. Launch the recorder : ./bin/recorder.bat or ./bin/recorder.sh
3. Configure where the test must be generated, for that you can change the fields "package", "class name", and "ouput folder" :

Gatling recorder configuration

4. Configure your web browser to use Gatling as a proxy. As see in the recorder configuration, the http port is 8000 and the https port is 8001.

Web browser configuration
5. Press the start button in the recorder
6. Go to the pages you want to test in the web browser
7. Press the stop & save button in the recorder
8. Open the generated test. In this example the test has been generated under C:\gatling-stress-tests\com\mycompany\stresstests\MyFirstStressTest.scala
9. Delete the useless requests. For example in my stress test I don't want the requests retrieving the static resources or the google analytics requests.
10. Rename the scenario and the http methods giving comprehensive titles : we will find these titles in the test reports.
11. Change the count of users to do 50 simultaneous connections to the webapp

Congratulations you have created your first Gatling test :

import io.gatling.core.Predef._
import io.gatling.core.session.Expression
import io.gatling.http.Predef._
import io.gatling.jdbc.Predef._
import io.gatling.http.Headers.Names._
import io.gatling.http.Headers.Values._
import scala.concurrent.duration._
import bootstrap._
import assertions._

class MyFirstStressTest extends Simulation {
  val httpProtocol = http.baseURL("http://mywebsite.com")
                         .acceptHeader("image/png,image/*;q=0.8,*/*;q=0.5")
                         .acceptEncodingHeader("gzip, deflate")
                         .acceptLanguageHeader("fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3")
                         .connection("keep-alive")
                         .userAgentHeader("Mozilla/5.0 (Windows NT 6.1; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0")
  val headers_1 = Map("""Accept""" -> """text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8""")
  val headers_5 = Map("""Accept""" -> """text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8""",
                      """Content-Type""" -> """application/x-www-form-urlencoded""")
  
  val scn = scenario("my first test")
     .exec(http("home page").get("""/""").headers(headers_1))
     .exec(http("login page").get("""/Account/Login""").headers(headers_1))
     .exec(http("login").post("""/Account/Login""").headers(headers_5)
                        .param("""UserName""", """jsebfranck""")
                        .param("""Password""", """my password""")
     .exec(http("news").get("""/News""").headers(headers_1))

  setUp(scn.inject(atOnce(50 user))).protocols(httpProtocol)
}

As you can see, a Gatling test is a Scala script. Don't worry if you don't know Scala, this script is easy to understand, you don't have to understand all Scala mechanisms :
  • httpProtocol variable defines the global parameters to connect to the website
  • headers_1 and headers_5 defines http headers parameters 
  • scn is the test scenario with the title "my first test"
  • the test contains 4 http calls (3 GET and 1 POST) called "home page", "login page", "login" and "news"
  • setUp allows to launch the test
  • atOnce(50 user) indicates that 50 connections will be done simultaneously

Launch the test

Launch a Gatling test is very easy :

1. Launch the gatling executable : ./bin/gatling.bat or ./bin/gatling.sh
2. Select the MyFirstStressTest test
3. Open the HTML report generated in results folder

Several graphics are generated in the report. But two graphics are particularly interesting. The first one give for each HTTP request :
  • the minimum, maximum and average response time
  • the standard deviation : deviation of the requests response time compared to the average response time. A small standard deviation means that the response time is almost the same for each request
  • the 95 percentile : means that 95% of the requests are under this response time 
  • the 99 percentile : means that 99% of the requests are under this response time



If you click on a request title, for example "news", you get more information specific to the request. Here we can see the response time evolution based on the active sessions.


Conclusion

I hope you understood it is not complicated to use a stress test tool like Gatling, even if it is rare for you to do performance benchmarks. This example is very basic and doesn't show all great features of Gatling. If you want to go further, I encourage you to read the following documentations :

dimanche 25 août 2013

EJB 2 on Jboss 7.1 example

I recently have add the opportunity to migrate an historical JEE web application from JBoss 4 to JBoss 7.1. One of the more complicated point I faced was the migration of the EJB2 components. The old xml configuration was not correct anymore and it was very difficult to find documentation, tutorials or concrete examples, even on JBoss forums. Of course this is understandable because this old version of EJB is not used anymore (hopefully!), but it still exists in several historical web applications.

The goal of this new thread is not to explain all the EJB 2 mechanisms but to share what I really missed during the migration, that is a working example of EJB2 sessions and EJB2 entities on JBoss 7.1. Since JEE 6, it is possible to package the EJB directly in a war archive, so this example will be based on that.

The source code is available here.

EJB 2 session java code

I will create an EJB 2 session called HelloWorld with a simple method returning a String object. Three classes must be created to do that :

  • The EJB interface
package com.jsebfranck.jboss.ejb2.session;

import java.rmi.RemoteException;
import javax.ejb.EJBObject;

public interface HelloWorldEJB extends EJBObject {
 public String helloWorld() throws RemoteException;
}

  • The implementation
package com.jsebfranck.jboss.ejb2.session;

import java.rmi.RemoteException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;

public class HelloWorldEJBBean implements SessionBean {
 private static final long serialVersionUID = 1L;

 public String helloWorld() throws RemoteException {
  return "Hello world, I am an EJB2 session";
 }
 
 public void ejbActivate() throws EJBException, RemoteException {}
 public void ejbPassivate() throws EJBException, RemoteException {}
 public void ejbRemove() throws EJBException, RemoteException {}
 public void setSessionContext(SessionContext arg0) throws EJBException, RemoteException {}
}

  • And the EJB Home
package com.jsebfranck.jboss.ejb2.session;

import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;

public interface HelloWorldEJBHome extends EJBHome {
 public HelloWorldEJB create() throws RemoteException, CreateException;
}

EJB 2 entity java code

For the EJB 2 entity example, I will create an object called Member with two fields : an id and a login. Three java objects are also needed to do that :

  • The EJBLocalObject
package com.jsebfranck.jboss.ejb2.entity;

import javax.ejb.EJBLocalObject;

public interface Member extends EJBLocalObject {
    public Long getId();
    public String getLogin();
}
  • The EntityBean
package com.jsebfranck.jboss.ejb2.entity;

import javax.ejb.CreateException;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;

public abstract class MemberBean implements EntityBean {

  private static final long serialVersionUID = 1L;
  private transient EntityContext ctx;

  public MemberBean() {
  }

  public Long ejbCreate(Long id, String login) throws CreateException {
    setId(id);
    setLogin(login);
    return id;
  }

  public void ejbPostCreate(Long id, String login) {
  }

  public abstract Long getId();
  public abstract void setId(Long id);

  public abstract String getLogin();
  public abstract void setLogin(String login);

  public void setEntityContext(EntityContext ctx) {
    this.ctx = ctx;
  }

  public void unsetEntityContext() {
    this.ctx = null;
  }

  public void ejbActivate() {}
  public void ejbPassivate() {}
  public void ejbLoad() {}
  public void ejbStore() {}
  public void ejbRemove() {}
}

  • And the EJBHome
package com.jsebfranck.jboss.ejb2.session;

import java.rmi.RemoteException;

import javax.ejb.CreateException;
import javax.ejb.EJBHome;

public interface HelloWorldEJBHome extends EJBHome {
 public HelloWorldEJB create() throws RemoteException, CreateException;
}

XML configuration


Now we have to declare the EJB entity and the EJB session in the xml configuration. A first file is needed for that, ejb-jar.xml. This file must be put in the /WEB-INF folder of the war archive.

<ejb-jar version="3.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://java.sun.com/xml/ns/javaee" 
xsi:schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd">
  <enterprise-beans>
    <session>
      <ejb-name>HelloWorldEJB</ejb-name>
      <home>com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome</home>
      <remote>com.jsebfranck.jboss.ejb2.session.HelloWorldEJB</remote>
      <ejb-class>com.jsebfranck.jboss.ejb2.session.HelloWorldEJBBean</ejb-class>
      <session-type>Stateless</session-type>
      <transaction-type>Container</transaction-type>
    </session>
    <entity>
      <ejb-name>MemberEJB</ejb-name>
      <local-home>com.jsebfranck.jboss.ejb2.entity.MemberHome</local-home>
      <local>com.jsebfranck.jboss.ejb2.entity.Member</local>
      <ejb-class>com.jsebfranck.jboss.ejb2.entity.MemberBean</ejb-class>
      <persistence-type>Container</persistence-type>
      <prim-key-class>java.lang.Long</prim-key-class>
      <reentrant>false</reentrant>
      <cmp-version>2.x</cmp-version>
      <abstract-schema-name>member</abstract-schema-name>
      <cmp-field>
        <field-name>id</field-name>
      </cmp-field>      
      <cmp-field>
        <field-name>login</field-name>
      </cmp-field>
      <primkey-field>id</primkey-field>
    </entity>
  </enterprise-beans>
</ejb-jar>

Now let's configure the mapping of the EJB entity. This is done in the jbosscmp-jdbc.xml file which must be put in the META-INF folder of the war archive.

<jbosscmp-jdbc>
  <defaults>
    <datasource>java:jboss/datasources/hsqldbDS</datasource>
  </defaults>
  <enterprise-beans>
    <entity>
      <ejb-name>MemberEJB</ejb-name>
      <row-locking>false</row-locking>
      <table-name>simple</table-name>
      <cmp-field>
        <field-name>id</field-name>
        <column-name>id</column-name>
      </cmp-field>      
      <cmp-field>
        <field-name>login</field-name>
        <column-name>login</column-name>
      </cmp-field>
    </entity>
  </enterprise-beans>
</jbosscmp-jdbc>


JBoss configuration

As you noticed in the jbosscmp-jdbc.xml file, a datasource is required for the entity. This datasource is configured directly in the jboss configuration. As I launch JBoss 7.1 in a standalone mode, I put the following configuration in the $JBOSS_HOME/standalone/configuration/standalone-full.xml file.

In this example, a use a hsql database :

<subsystem xmlns="urn:jboss:domain:datasources:1.0"> <datasources> <datasource enabled="true" jndi-name="java:jboss/datasources/hsqldbDS" jta="true" pool-name="hsqldbDS" use-ccm="true" use-java-context="true"> <connection-url>jdbc:hsqldb:file:/Users/jsebfranck/Documents/database/standaloneHsqldb</connection-url> <driver>hsqldb</driver> <pool> <prefill>false</prefill> <use-strict-min>false</use-strict-min> <flush-strategy>FailingConnectionOnly</flush-strategy> </pool> <security> <user-name>sa</user-name> </security> </datasource>


Deployment in jboss

When you deploy the web application on jboss (standalone.sh --server-config=standalone-full.xml), you can see the JNDI names of both EJB in the logs. This will help us to do our lookups in the client.


19:07:56,689 INFO  [org.jboss.as.server.deployment] (MSC service thread 1-2) JBAS015876: Starting deployment of "WarWithEJB2.war"
19:07:57,069 INFO  [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-4) JNDI bindings for session bean named MemberEJB in deployment unit deployment "WarWithEJB2.war" are as follows:

 java:global/WarWithEJB2/MemberEJB!com.jsebfranck.jboss.ejb2.entity.Member
 java:app/WarWithEJB2/MemberEJB!com.jsebfranck.jboss.ejb2.entity.Member
 java:module/MemberEJB!com.jsebfranck.jboss.ejb2.entity.Member
 java:global/WarWithEJB2/MemberEJB!com.jsebfranck.jboss.ejb2.entity.MemberHome
 java:app/WarWithEJB2/MemberEJB!com.jsebfranck.jboss.ejb2.entity.MemberHome
 java:module/MemberEJB!com.jsebfranck.jboss.ejb2.entity.MemberHome

19:07:57,073 INFO  [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-4) JNDI bindings for session bean named HelloWorldEJB in deployment unit deployment "WarWithEJB2.war" are as follows:

 java:global/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJB
 java:app/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJB
 java:module/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJB
 java:jboss/exported/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJB
 java:global/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome
 java:app/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome
 java:module/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome
 java:jboss/exported/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome

Simple servlet client

We are now able to test our EJBs. The following code calls the helloWorld method of the EJB session, then it creates a new line in the Member table.

package com.jsebfranck.jboss.servlet;

import java.io.IOException;

import javax.naming.InitialContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.jsebfranck.jboss.ejb2.entity.Member;
import com.jsebfranck.jboss.ejb2.entity.MemberHome;
import com.jsebfranck.jboss.ejb2.session.HelloWorldEJB;
import com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome;

@WebServlet("/Ejb2Servlet")
public class Ejb2Servlet extends HttpServlet {
  private static final long serialVersionUID = 1L;

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    try {
      // EJB 2 session
      HelloWorldEJBHome helloWorldEJBHome = (HelloWorldEJBHome) new InitialContext().lookup("java:global/WarWithEJB2/HelloWorldEJB!com.jsebfranck.jboss.ejb2.session.HelloWorldEJBHome");
      HelloWorldEJB helloWorldEjb = helloWorldEJBHome.create();
      response.getWriter().println("EJB session test : " + helloWorldEjb.helloWorld());

      // EJB 2 entity
      MemberHome memberHome = (MemberHome) new InitialContext().lookup("java:global/WarWithEJB2/MemberEJB!com.jsebfranck.jboss.ejb2.entity.MemberHome");
      Member member = memberHome.create(25L, "jsebfranck");
      response.getWriter().println("EJB entity test : " + member.getId() + " - " + member.getLogin());
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
}

And we have the prove that this example is working :-)


Conclusion

I hope this example helped you to migrate your EJB2 to a newer version of your application server. Don't hesitate to contact me if you need further details to advance in your migration.