How to become a better developer!

The fast and constant change in technology made it inevitable for developers to always improve their skills through continuous learning. It is a fact now that developers with outdated skills will be left behind!
Learning isn’t the same for everyone. Some people may learn well by themselves; some may need instruction and example. In the programming world, many resources can meet those particular needs.
And using skills on projects that aren’t all imaginary is key too. Real world projects equal real world experience that translates into adapting more efficient, effective programming language.
What else helps to improve programmers’ skills and help them continue to move into the next wave-whatever that may be? I found this graphic that explains tactics, solutions, and ideas to pursue.
Personally, I have been following some of those for the past couple of years to improve my technical and soft skills!

Click to Enlarge Image

’8

How to handle Java exceptions in clean code? – Part 2

In my previous post, I explained the different types of exceptions in Java and left two questions for this post!

Which exception to use?

Java documentation and Uncle Bob’s book (Clean Code) were my references when preparing this tutorial. I noticed a slight difference between what is recommended in each!

Java Documentation

While reading the Java documentation, you sense a preference for Checked Exceptions. The documentation states that the usage of UnChecked Exceptions should be restricted to the case where crashing the system is intentional if an exception occurs. In other cases where recovery is still possible, Checked Exceptions should be used.

Robert Martin’s opinion

Uncle Bob has a different opinion!  He argues that although Checked Exceptions might have some benefits and can be useful in some special cases like writing critical libraries, they are not a necessity to have a robust software.

Breaking the ‘Open/Closed Principle‘ and ‘Encapsulation‘ are the main two reasons that make using Checked Exceptions a bad idea!

Let’s see how!

In the below example, the MainBookReader.main method is calling the method MainBookReader.readJsonObject to get the book’s description from a JSON file. And since readJsonObject throws an exception we had to add throws FileNotFoundException clause to the signature of the main method and the interface JsonLoader!

import java.io.FileNotFoundException;

public class MainBookReader {
    public static void main(String[] args) throws FileNotFoundException {
        BookJsonLoader jsonLoader = new BookJsonLoader();
        Book bookFromJson = jsonLoader.readJsonObject("books.json");
        System.out.println(bookFromJson.name());
    }
}
import com.google.gson.Gson;
import java.io.FileNotFoundException;
import java.io.FileReader;

public class BookJsonLoader implements JsonLoader {
    public Book readJsonObject(String fileName) throws FileNotFoundException {
        FileReader jsonFile = new FileReader(fileName);
        Gson gson = new Gson();
        return transformToBook(gson.fromJson(jsonFile, JsonBook.class));
    }

    private Book transformToBook(JsonBook jsonBook) {
        return new Book(jsonBook.getName());
    }
}
import java.io.FileNotFoundException;

public interface JsonLoader {
    Book readJsonObject(String fileName) throws FileNotFoundException;
}

Adding the throws clause to most of our methods means two things!

  1. Our top methods & classes had to know a lot about the lower methods & classes!
  2. If any change is to be applied to the lower methods, this change has to be replicated to all above methods!

This is a violation of the ‘Open/Closed Principle‘ and ‘Encapsulation‘!

Is there a better way?

No matter how careful we are, things can still go wrong. Thus we still need to deal the exceptions when they occur! The alternative to the code above is writing clean code!

In this section, I will refactor the above code to make the code look better, although it might involve more code!

Wrap the Exception

The first step is to wrap the exception in one place! In our case, we can replace the throws clause with a try-catch!

In the catch clause, I’m throwing a JsonFileNotFoundException (UncheckedException) that I have created based on the needs of this system (i.e. providing an informative message to the user).

public class BookJsonLoader implements JsonLoader {
    public Book readJsonObject(String fileName){
        try {
            FileReader jsonFile = new FileReader(fileName);
            Gson gson = new Gson();
            return transformToBook(gson.fromJson(jsonFile, JsonBook.class));
        } catch (FileNotFoundException e) {
            throw new JsonFileNotFoundException("Can't find the file " + fileName + ". Please make sure it exists!", e);
        }
    }

    private Book transformToBook(JsonBook jsonBook) {
        return new Book(jsonBook.getName());
    }
}

The benefits of this refactoring are:

  • Remove all ‘throws clauses.’
  • Hide the logic and information of our class from the outer classes
  • Send an informative message to the user in case the file was not found
  • Minimize the dependency (remove imports from other classes)

Don’t return null

In some cases, developers don’t want to throw an UnCheckedException, so they tend to return a ‘null’ instead! That is a bad practice because it will eventually result in a NullPointerException later.

So, what to do? The answer is simple, return a SPECIAL CASE!

The below class ‘MissingBook‘ is our special case!

public class MissingBook extends Book {

    private MissingBook(String name) {
        super(name);
    }

    public static MissingBook aMissingBook() {
        return new MissingBook("MISSING BOOK");
    }
}

This allows us to return an instance of the MissingBook in the catch instead of throwing an exception!

public class BookJsonLoader implements JsonLoader {
    public Book readJsonObject(String fileName){
        try {
            FileReader jsonFile = new FileReader(fileName);
            Gson gson = new Gson();
            return transformToBook(gson.fromJson(jsonFile, JsonBook.class));
        } catch (FileNotFoundException e) {
            return MissingBook.aMissingBook();
        }
    }

    private Book transformToBook(JsonBook jsonBook) {
        return new Book(jsonBook.getName());
    }
}

Don’t pass null

In your code, you shouldn’t pass null as parameters and instead use the Special Cases as before. But, it gets harder to prevent your clients from passing nulls thus you might need to assert some function parameters before proceeding with your code execution!

Finally

Doing the refactoring for the simple example above might look like over-engineering! But, it becomes beneficial when writing more complex code!

I think that the excessive usage of CheckedExceptions will pollute the code and thus should be avoided! For me, the benefits of CheckedExceptions can still be achieved through:

  1. Following the rules of Clean Code
  2. Increase test coverage of the code by following the TDD practice! With the use of TDD, all the edge cases should be covered and thus eliminating the possibility of having unexpected exceptions during execution!

To understand better the importance and how to write clean code, it is highly recommended that you read the book Clean Code by Robert Martin!

References:

  1. Clean Code: A Handbook of Agile Software Craftsmanship
  2. Unchecked Exceptions — The Controversy
  3. Java Exceptions
  4. The Clean Code Blog

How to handle Java exceptions in clean code? – Part 1

Reading the chapter ‘Error Handling‘ from the book ‘Clean Code‘ made me think whether developers follow the clean code rules when writing production code. It also appeared to me that the difference between the Java exception types might not clear be for some!

The purpose of this short tutorial is to clarify those two points. And to make it clearer, I will be dividing it into two posts. In the first one, I will explain the different types of Java exceptions. Whereas in the second one, I will show how to write clean exception code!

Java Exceptions

In Java, the class Throwable is at the top of the class hierarchy of all exceptions. The classes Error and Exception directly extend Throwable. All the subclasses of Error and Exception are grouped into two types ‘Checked Exceptions‘ and ‘UnChecked Exceptions‘as displayed in the image below.

javaexceptiondiagram

What is the difference between Checked and UnChecked Exceptions?

UnChecked Exceptions

UnChecked Exceptions extend the classes ‘Error‘ or ‘RuntimeException‘. In this case, developers don’t have to worry about catching or handling the exceptions at compile time as the compiler won’t report any error. But, as they are not caught, those exceptions may result in complete failure of the application when thrown during execution.

Below are two examples of UnChecked Exceptions!

1. Runtime Exception

The below method simply divides two numbers:

private static int divide(int numerator, int denominator) {
    return numerator / denominator;
}

The code is fine and will compile with no errors! So what is the problem?

The problem might occur at runtime if we try to call the method while passing zero as the denominator. Since this exception is not caught, our system will crash throwing the below arithmetic exception:

Exception in thread "main" java.lang.ArithmeticException: / by zero
 at MainUnCheckedException.divide(MainUnCheckedException.java:7) ...
2. Error

The below method compiles, but since I missed writing the base cases calling the method will crash the system and throw a StackOverflowError (as shown below) will throw an Error!

private static int fibonacci(int number) {
    return fibonacci(number - 1) + fibonacci(number - 2);
}
Exception in thread "main" java.lang.StackOverflowError
 at MainUnCheckedException.fibonacci(MainUnCheckedException.java:10)...

Checked Exceptions

On the other hand, all other classes extending the ‘Exception‘ class are considered to be Checked Exceptions. In this case, the code will not compile if the developer doesn’t explicitly handle the exception.

This adds a level of security to your code, as you are forced to specify how your code should behave when an exception occurs and thus decreases the chance of having an unrecoverable failure in the system.

Let’s see an example!

public class MainCheckedException {
    public static void main(String[] args) {
        FileInputStream fileInputStream = new FileInputStream("foo.txt");
    }
}

Error:(6, 43) java: unreported exception java.io.FileNotFoundException;
must be caught or declared to be thrown

In the above code, we are trying to read a file ‘foo.txt’, but our compiler complains that we need to handle the FileNotFoundException.

Solving this compilation error can be done in two ways:

  1. Try Catch: The first solution requires surrounding the code with a try-catch.
    private static void readFileTryCatch() {
        try {
            FileInputStream fooFile = new FileInputStream("foo.txt");
        } catch (FileNotFoundException e) {
            System.out.println(e.getMessage());
        }
    }

    Even if the file was not there, this code would not break thanks to the try-catch. As shown in the below code and output, the system will print out the exception and continue execution!

    public static void main(String[] args) {
        readFileTryCatch();
        System.out.println("Done");
    }
    ------------Output------------
    foo.txt (No such file or directory)
    Done
  2. Throw an exception: The other solution is adding a ‘throws FileNotFoundException‘ to the method signature.
    private static void readFileThrow() throws FileNotFoundException {
        new FileInputStream("foo.txt");
    }

    Using this solution will propagate handling the exception to the calling methods as illustrated in the two options below:
    Option 1: Adding throws to all methods signatures 

    public static void main(String[] args) throws FileNotFoundException {
        readFileThrow();
        System.out.println("Done");
    }
    ------------Output------------
    Exception in thread "main" java.io.FileNotFoundException: foo.txt (No such file or directory)
    at java.io.FileInputStream.open0(Native Method) ...

    Option 2: Catching the exception in one of the methods in the call stack

    public static void main(String[] args)  {
        try {
            readFileThrow();
        } catch (FileNotFoundException e) {
            System.out.println(e.getMessage());
        }
        System.out.println("Done");
    }
    ------------Output------------
    foo.txt (No such file or directory)
    Done

 

I hope this post gave you a better understanding of Java exceptions! In the 2nd part, I will be sharing more details on how to properly write exceptions in clean code and which of the two exception types to use!

Using H2 In-Memory to test your DAL

How should we test the Data Access Layer code?

Many developers ask that question. Similar to the other layers of your system, it should be fully tested to prevent unexpected and random behavior in production. There are many ways to achieve that, among of which are mocks or in-memory database.

The problem with mocks is that instead of testing the validity of your SQL queries (syntax and execution), you will only be testing the validity of the system’s flow. On the other hand, using the in-memory database will validate both! That is why I prefer it over mocking. But, you should keep in mind that the SQL syntax might differ from one database engine to another.

In this blog, I will be giving an example of using “H2 In-Memory” in unit tests. The code of this example is available on my GitHub account.

H2 In-Memory in Action

So, let us see H2 in action 🙂

For the sake of this blog, I will assume we want to test three SQL operations on the table “Members” having the following model:

MEMBERS
ID      | NAME        |
INTEGER | VARCHAR(64) |

The SQL operations to be covered in this blog are:

  1. Create Table
  2. Insert (batch of prepared statements)
  3. Select *

Code Explanation

The example will be based on five files (pom.xml, Member.java, SqlRepository.java, H2Repository.java and H2MembersRepositoryTest). In this section, I will give a brief explanation of each file.

Pom.xml (Maven Dependencies):

First, let us modify our pom file (as shown below) to make our project depend on two projects:

  1. com.h2database: Using this dependency, Maven will take care of downloading the h2 jar file we will be referencing in our tests.
  2. Junit: a unit testing framework for Java
<dependencies>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.4.191</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
    </dependency>
</dependencies>

Member.java:

This class represents a Member record. It consists of a factory method that returns an instance of the Member class and two getter methods to return the values of the Id and Name fields.

package dal;

import java.util.Objects;

public final class Member {
    private final int id;
    private final String name;

    private Member(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public static Member aMember(int id, String name) {
        return new Member(id, name);
    }

    public int id() {
        return id;
    }

    public String name() {
        return name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Member member = (Member) o;
        return id == member.id &&
                Objects.equals(name, member.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

SqlRepository.java

In this class, we establish a connection to our database. What is important in this class is that we pass the connection string as a parameter to the constructor thus making our code unbounded to any specific database engine (MySql, H2, Sybase, Oracle, etc.). This will make writing our tests much easier!

The class consists of:

  1. Constructor: initializes an instance of SQL Connection using the connection string passed as parameter
  2. Three public methods:
    1. createTable: executes an update query to create the”MEMBERS” table.
    2. allMemebers: executes a select query and returns the found records in a list of Members.
    3. insertMembers: takes a list of “Member” as a parameter and inserts the values into the “MEMBERS” table.
package dal;

import dal.Member;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class SqlRepository {

    protected final Connection connection;
    private static final String CREATE_MEMBERS = "CREATE TABLE MEMBERS(ID INTEGER, NAME VARCHAR(64))";
    private static final String SELECT_MEMBERS = "SELECT * FROM MEMBERS";
    private static final String INSERT_MEMBERS = "INSERT INTO MEMBERS(ID, NAME) VALUES(?, ?)";

    public SqlRepository(String connectionString) throws SQLException {
        connection = DriverManager.getConnection(connectionString);
    }

    public boolean createTable() throws SQLException {
        Statement createStatement = connection.createStatement();
        return createStatement.execute(CREATE_MEMBERS);
    }

    public List<Member> allMembers() throws SQLException {
        List<Member> allMembers = new ArrayList<>();
        Statement selectStatement = connection.createStatement();
        ResultSet membersResultSet = selectStatement.executeQuery(SELECT_MEMBERS);
        while (membersResultSet.next()) {
            allMembers.add(Member.aMember(membersResultSet.getInt(1), membersResultSet.getString(2)));
        }
        return allMembers;
    }

    public void insertMembers(List<Member> members) throws SQLException {
        final PreparedStatement insertMembers = connection.prepareStatement(INSERT_MEMBERS);
        members.forEach(member -> insertMember(member, insertMembers));
        insertMembers.executeBatch();
    }

    private void insertMember(Member member, PreparedStatement insertMembers) {
        try {
            insertMembers.setInt(1, member.id());
            insertMembers.setString(2, member.name());
            insertMembers.addBatch();
        } catch (SQLException e) {
            throw new UnsupportedOperationException(e.getMessage());
        }
    }
}

H2Repository.java

I added this class under “Test Sources Root” because it is only used by the tests.

As you notice, it extends the SqlRepository class implemented previously. Thus, we don’t have a lot to implement here. The only method added is a new method “closeConnection” that drops all the existing tables from the database.

You might wonder why would we need that since we are using an In-Memory database. That might be true for this simple example, but it will be a necessity when running multiple tests classes. That is because, in Java, all the tests are run in the same JVM which means that the H2 instance initialized in the first test will be shared with the next test classes. This approach might lead to an unexpected behavior when using the same tables in the different test classes.

package dal;

import java.sql.SQLException;

public final class H2Repository extends SqlRepository {
    public H2Repository(String connectionString) throws SQLException {
        super(connectionString);
    }

    public void closeConnection() throws SQLException {
        connection.createStatement().execute("DROP ALL OBJECTS");
    }
}

H2MembersRepositoryTest.java

This is the simple test class! Our single test (it_correctly_inserts_members_to_a_database) is invoking the three methods we implemented before (createTable, insertMember and allMembers). If any of those methods is badly written our test would fail.

package dal;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import static dal.Member.aMember;
import static org.fest.assertions.Assertions.assertThat;

public class H2MembersRepositoryTest {

    private static final String H2_CONNECTION_STRING = "jdbc:h2:mem:test";
    private static final List<Member> MEMBERS = new ArrayList<>(10);
    private static H2Repository h2Repository;

    @BeforeClass
    public static void
    setup_database() throws SQLException {
        h2Repository = new H2Repository(H2_CONNECTION_STRING);
        initializeMembers();
    }

    @Test
    public void
    it_correctly_inserts_members_to_a_database() throws SQLException {
        h2Repository.createTable();
        h2Repository.insertMembers(MEMBERS);

        assertThat(h2Repository.allMembers()).isEqualTo(MEMBERS);
    }

    private static void initializeMembers() {
        for (int index = 0; index < 10; index++) {
            MEMBERS.add(aMember(index, "Name_" + index));
        }
    }

    @AfterClass
    public static void
    tear_down_database() throws SQLException {
        h2Repository.closeConnection();
    }
}

TearDown

If you can test your DAL, you can test anything! (I just came up with this ;))

Keep the tests going!

References:

  1. H2-Database Engine
  2. H2-Maven
  3. JUnit Maven
  4. GitHub

How to move a git repository to subdirectory of another repository

You might come across a situation when working with Git where you want to move a repository to another repository (i.e., make it a subdirectory of the other` repository) while maintaining its full history.

In this blog, I will be pointing out step by step with an example how to achieve that.

Use Case

Let us assume we have two repositories on git, repoA, and repoB, and we want to make repoB a subdirectory of repoA while maintaining its full history.

To achieve that we will move the content of repoB into a subfolder under repoB and then merge repoA and repoB. 

Bellow is the detailed git process:

  1. The first step is to clone repoA and repoB on your machine
    $ git clone repoA
    $ git clone repoB

    Note: For the purpose of this blog I submitted a README.md file in each of the two repositories on Bitbucket. Below are two snapshots are taken for both repositories:

    RepoA:
    REPO-A-Commits

    RepoB:
    REPO-B-Commits

  2. The second step is to copy the content of repoB to a subfolder. Go to the folder repoB and apply the following steps:
    • create a subfolder repoB
      $ mkdir repoB
    • move everything from the parent repoB to the child repoB (except the .git folder)
    • stage the files (the added folder and deleted file(s)) for a later commit
      $ git stage repoB/
      $ git stage README.md
    • commit and push those changes to git
      $ git commit -am '[REPO-B] Move content to a subfolder'
      $ git push origin master
  3. Go to the folder repoA and do the following:
    • add a remote branch with the content of repoB
      $ git remote add repoBTemp (path_to_repoB)
    • fetch repoBTemp the temp repo we created in the previous step
      $ git fetch repoBTemp
    • merge repoBTemp with repoA
      $ git merge repoBTemp/master
    • delete the remote repoBTemp
      $ git remote rm repoBTemp
    • push the changes to the server

      $ git push origin master

  4. Now we can check our git history to verify that our trick worked as intended. In the right image below you can see that the history of repoB is now part of repoA’s history. In the left image, you can notice that repoB is now a subdirectory of repoA.
  5. You it should be sage to delete the repository repoB

I hope you find this blog helpful!

 

 

How to reference Ruby-JMeter from Java project?

INTRODUCTION

Apache JMeter is an open-source Java application that allows you to design, build and run performance tests. It is an excellent tool and currently we are using it to analyze our software’s performance. But being a graphical tool, after building a couple of tests you will notice that most of the test steps were repeated each time. It becomes frustrating and a waste of time and here is when Ruby-JMeter comes to the rescue!!! Ruby-JMeter is a tool that allows you to write your JMeter test plan in Ruby and then convert it to a JMX file. Since it is an open-source project, you have the flexibility of extending it and customizing it to fit your needs.

How can this tutorial benefit you?l

The purpose of this tutorial is to show how to integrate ruby-jmeter within a Java project. We are using this integration in an in-house Java based benchmark framework that dynamically reads and executes JMX files. Another way to benefit from this tutorial is if you want to reference any of the RubyGems from your application. Ruby-jmeter is one of the gems hosted on RubyGems, thus, you can follow the steps defined in the 2nd part of the tutorial to reference other gems.

Tutorial Structure

I have divided this tutorial into three sections. The first one shows how to execute ruby scripts directly from Java; the second section shows how to ruby ruby-jmeter scripts from Java. Whereas the last section is dedicated from some refactoring of our test class.

Executing Ruby Scripts

1. Maven Dependency

One of the possible options to run Ruby from Java is by using JRuby.
Since we are writing a maven project, then we need to add JRuby dependency in our pom. To do that we just add the following dependency to the dependencies section of our pom.Xml file.

<dependency>
    <groupId>org.jruby</groupId>
    <artifactId>jruby-complete</artifactId>
    <version>9.0.3.0</version>
</dependency>

2. RubyExecutor

Now we can reference JRuby in our code, so let us start by adding a new java file “RubyExecutor” to our project. In this class, we will add a static method that takes the full path of the ruby script as a parameter.

Below is the code for the method:

public class RubyExecutor {
    public static void run(String rubyFile) throws FileNotFoundException, ScriptException {
         final ScriptEngine scriptEngine = new JRubyEngineFactory().getScriptEngine();
         final FileReader rubyFileReader = new FileReader(rubyFilePath);
         scriptEngine.eval(rubyFileReader);
         rubyFileReader.close();
    }
}

The first line initializes an instance of JRubyEngine.
The third line just calls the eval method that is responsible for executing the ruby code in the JRubyEngine.

3. Testing our code

The best way to assert our code is working as we want, is by writing JUnit tests. In this case, we can add a new test class RubyExecutorTest.java under “src/test/java”.
For simplicity, we will make our ruby code generate a new file at the end of its execution. This generated file has the same filename but with “jmx” as an extension.
Based on that we should make our test call the method RubyExecutor.run() and assert that a new file has been added.
First we should start by writing a simple ruby script in the test-resources folder.

Steps:

  1. Add a new folder “src/test/resources/RubyExecutor”
  2. Add a new ruby file “simpleCode.rb” under that folder
  3. Add the following code to the Ruby script. This code creates a new empty file with the name “simpleCode.jmx”
`File.new('target/test-classes/RubyExecutor/simpleCode.jmx', "w+")`

Now we can write our test class that should look like this:

public class RubyExecutorTest {
    @Test
    public void
    it_should_generate_a_file_from_ruby_code() throws IOException, ScriptException {
       final String rubyFile = this.getClass().getResource("/RubyExecutor/simpleCode.rb").getFile();
       RubyExecutor.run(rubyFile);
       final File expectedFile = new File(rubyFile.replace("rb", "jmx"));
       Assertions.assertThat(expectedFile).exists();
    }
}

The test asks the RubyExecutor to execute the ruby script named “simpleCode.rb” (the one we created in previous steps) and asserts that the file “simpleCode.jmx” has been created.
Now let us try to run the test using maven command. To do so run the below command from the directory “jruby-jmeter-integration”

mvn clean install

The tests should be green, and we should get the following success message:

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:17 min

We should also notice that the file “simpleCode.jmx” has been created under “target/test-classes/RubyExecutor/”
Note: I use the library org.fest.assertions.Assertions, thus make sure you have added it to your dependencies in maven.

Executing Ruby-JMeter Scripts

Now that we are capable of executing Ruby scripts from our project, we will work on running ruby-jmeter scripts.

1. Ruby-JMeter

As stated earlier, ruby-jmeter is a tool that allows you to define your jmeter test-plan in Ruby code. The tool provides methods for most of the JMeter controllers, thus, it is a matter of calling the right method with the right parameters. Upon execution, the tool will generate a JMX file from the Ruby code.
I’m not going to dig into details on how it works, for more details you can have a look at some examples available on project’s workspace on GitHub

Now it is time to write a simple test plan with ruby-jmeter.

Steps:

  1. Add a new ruby script “src/test/resources/RubyExecutor/simpleJmx.rb”
  2. Add the following code to the Ruby script:
require 'rubygems'
require 'ruby-jmeter'   
test do
  threads count: 1, rampup: 0, loops: 1, scheduler: false do
    beanshell_sampler query:'log.info("********Hello World********");'
  end
end.jmx(file: 'target/test-classes/RubyExecutor/simpleJmx.jmx')

This ruby code will do the following:

  1. Create a new test plan
  2. Add a new “Thread Group” with a single thread and single loop count
  3. Adds a BeanShellSampler as a child of the thread group to print “********Hello World********”
  4. Saves the test-plan in the file simpleJmx.jmx

The generated JMX file should look like this:

SimpleJmx

2. Ruby-JMeter within our maven project

Now we know exactly what our code should do; what are its input and output. So, it would be great if we write a JUnit test to check if our code has indeed generated a JMX file from the Ruby script we wrote in the previous section.
So our test should execute the ruby script and is expected to generate the respective JMX file (“target/test-classes/RubyExecutor/simpleJmx.jmx”) at the end of the execution.
Based on this, we can add a new test to RubyExecutorTest.java

@Test
public void
it_should_generate_a_jmx_file_from_ruby_code() throws IOException, ScriptException {
 final String rubyFile = this.getClass().getResource("/RubyExecutor/simpleJmx.rb").getFile();
 RubyExecutor.run(rubyFile);
 final File expectedFile = new File(rubyFile.replace("rb", "jmx"));
 Assertions.assertThat(expectedFile).exists();
}

Let us give it a try:

mvn clean install

Unfortunately if fails:

LoadError: no such file to load -- ruby-jmeter
    require at org/jruby/RubyKernel.java:939
    require at uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/rubygems/core_ext/kernel_require.rb:54
        <top> at <script>:2
    Tests run: 2, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 11.657 sec <<< FAILURE!
    it_should_generate_a_jmx_file_from_ruby_code(RubyExecutorTest)  Time elapsed: 11.446 sec  <<< ERROR!
    javax.script.ScriptException: org.jruby.embed.EvalFailedException: (LoadError) no such file to load -- ruby-jmeter
        at org.jruby.embed.jsr223.JRubyEngine.wrapException(JRubyEngine.java:104)
        at org.jruby.embed.jsr223.JRubyEngine.eval(JRubyEngine.java:121)
        at org.jruby.embed.jsr223.JRubyEngine.eval(JRubyEngine.java:146)

The exception is telling us that it couldn’t execute ruby-jmeter. Maven wasn’t able to locate the ruby-jmeter libraries as we haven’t referenced it yet in any of our code.
To fix this problem, we will have to add more dependencies to our pom file!

3. Ruby-Gems Dependency

RubyGems is a repository hosting Ruby community’s gems. Once referenced, Maven will access this repository to sync the required ruby libraries locally to your project.
In our case we are using the ruby-jmeter gems to add dependency on that we should modify our pom file as follows:

1.  Add repository: This will be used by maven to download the gems from the specified URL

     <repositories>
        <repository>
            <id>rubygems-releases</id>
            <url>http://rubygems-proxy.torquebox.org/releases</url>
        </repository>
    </repositories>

2.  Add dependency on ruby-jmeter

<dependency>
    <groupId>rubygems</groupId>
    <artifactId>ruby-jmeter</artifactId>
    <version>2.13.8</version>
    <type>gem</type>
</dependency>

3.  Add build step

<build>
    <plugins>
        <plugin>
            <groupId>de.saumya.mojo</groupId>
            <artifactId>gem-maven-plugin</artifactId>
            <version>1.1.3</version>
            <extensions>true</extensions>
            <executions>
                <execution>
                    <goals>
                        <goal>initialize</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <includeRubygemsInResources>true</includeRubygemsInResources>
            </configuration>
        </plugin>
    </plugins>
</build>

4.  Rerun maven:

mvn clean install

Now we should get a “Build Success.”

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running RubyExecutorTest
I, [2015-12-23T17:27:19.643000 #7564]  INFO -- : Test plan saved to: target/test-classes/RubyExecutor/simpleJmx.jmx
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 7.278 sec
Results :
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ java-ruby-jmeter-integration ---
[INFO] Building jar: D:\Dev\java\workspace\GitHub\sample-projects\jruby-jmeter-integration\target\java-ruby-jmeter-integration-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- maven-install-plugin:2.4:install (default-install) @ java-ruby-jmeter-integration ---
[INFO] Installing D:\Dev\java\workspace\GitHub\sample-projects\jruby-jmeter-integration\target\java-ruby-jmeter-integration-1.0-SNAPSHOT.jar to D:\Dev\maven_repo\aatwi\github\java-ruby-jmeter-integration\1.0-SNAPSHOT\java-ruby-jmeter-integration-1.0-SNAPSHOT.jar
[INFO] Installing D:\Dev\java\workspace\GitHub\sample-projects\jruby-jmeter-integration\pom.xml to D:\Dev\maven_repo\aatwi\github\java-ruby-jmeter-integration\1.0-SNAPSHOT\java-ruby-jmeter-integration-1.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

Code Refactoring

The last part of this tutorial is to do some code refactoring to our test class. Although it isn’t essential here, it became a habit for me!!
If we look at our current code, we can notice that there is some code duplication in the two tests.
One thing we can do here is extracting the method “assertJmxFileExists” to remove this duplication.
Our new method will take the responsibility of calling RubyExecutor.run and asserting the generation of the JMX file.
The code below shows the difference before and after refactoring.
Before Refactoring:

@Test
public void
it_should_generate_a_file_from_ruby_code() throws IOException, ScriptException {
    final String rubyFile = this.getClass().getResource("/RubyExecutor/simpleCode.rb").getFile();
    RubyExecutor.run(rubyFile);
    final File expectedFile = new File(rubyFile.replace("rb", "jmx"));
    Assertions.assertThat(expectedFile).exists();
}  

@Test
public void
it_should_generate_a_jmx_file_from_ruby_code() throws IOException, ScriptException {
    final String rubyFile = this.getClass().getResource("/RubyExecutor/simpleJmx.rb").getFile();
    RubyExecutor.run(rubyFile);
    final File expectedFile = new File(rubyFile.replace("rb", "jmx"));
    Assertions.assertThat(expectedFile).exists();
}

After Refactoring:

@Test
public void
it_should_generate_a_file_from_ruby_code() throws IOException, ScriptException {
    assertJmxFileExists("simpleCode.rb");
}

@Test
public void
it_should_generate_a_jmx_file_from_ruby_code() throws IOException, ScriptException {
    assertJmxFileExists("simpleJmx.rb");
}

private void assertJmxFileExists(final String rubyScript) throws IOException, ScriptException {
    final String rubyFile = this.getClass().getResource("/RubyExecutor/" + rubyScript).getFile();
    RubyExecutor.run(rubyFile);
    final File expectedJmxFile = new File(rubyFile.replace("rb", "jmx"));
    Assertions.assertThat(expectedJmxFile).exists();
}

We can do more refactoring, but I think it is not worth at this point. The code is simple and can be easily read and understood.
Finally, rerun maven to make sure that nothing was broken with this refactoring.

References