Wednesday, May 2, 2012

Configurable, and modular Java classes using Annotations and Enumerations

In todays blog post, I'm showing how I implemented highly configurable, and modular classes with the help of annotations, and enumerations. From now on, let's call these modular classes "modules". Let's suppose we have the following architectural problem:

  1. There is a need for modules that are highly configurable.
  2. The modules implement the same interface.
  3. The configuration parameters vary.
  4. Some configuration parameters are required.
  5. Some configuration parameters have a default value.
  6. A configuration parameter should have an explanation for the enduser.
  7. It should also be easy to add new modules.

UML Class Diagram

ModuleFactory and ModuleFactoryImpl

The module factory is responsible for creating a configured module. It takes the class name, and Properties object as parameters. I'm using a class name here instead of a class type, because the list of supported modules are stored in the database as names. The Properties object contains the configuration for the module to be created.

ModuleType

This enumeration is basically a list of supported types by the factory. When a new module is implemented, it should be added to this enumeration, otherwise the factory doesn't know how to translate the class name to the actual class. I tried to find a way to avoid the need of this enumeration, like searching in the class path for classes that correspond to the name. Unfortunately, this is too complex to achieve without adding a lot of boilerplate code. If you find a better way to solve this problem, please let me know in the comments.

Module

This interface formalizes the methods implemented by all modules. Their parameters are defined in an enumeration of type ModuleParameter. Every module should have a corresponding ModuleParameter enumeration. An example implementation for this demo is called "ExampleModule", and it uses "ExampleModuleParameter" to define its configuration.

ModuleParameter

This interface specifies the required methods for every subclass enumeration implementation. The enumeration is basically a list of configuration parameters for a module. Per configuration parameter, it also defines: if the parameter is required, the default value, and a brief explanation. An example implementaton for this demo is called "ExampleModuleParameter", and it has two configuration parameters.

The Implementation

ModuleParameter

package com.javaeenotes.modules;


// This interface enforces required methods for the parameter enumerator.
public interface ModuleParameter {

    boolean isRequired();

    String getDefaultValue();

    String getExplanation();
}

This interface defines the methods used to retrieve the properties of the configuration parameter.

ExampleModuleParameter

package com.javaeenotes.modules;


// This enum class contains the parameters used to configure a module.
public enum ExampleModuleParameter implements ModuleParameter {
    PARAM1("param1", true, "DEFAULT", "This is parameter 1."),
    PARAM2("param2", false, null, "This is parameter 2.");

    // Code below is boiler plate code. It is not possible to move the
    // code to an abstract class, because enumerators are not allowed
    // to extend classes.

    private final String name;
    private final boolean isRequired;
    private final String defaultValue;
    private final String explanation;


    private ExampleModuleParameter(
            String name,
            boolean isRequired,
            String defaultValue,
            String explanation) {

        this.name = name;
        this.isRequired = isRequired;
        this.defaultValue = defaultValue;
        this.explanation = explanation;
    }


    @Override
    public String toString() {
        return name;
    }


    @Override
    public boolean isRequired() {
        return isRequired;
    }


    @Override
    public String getDefaultValue() {
        return defaultValue;
    }


    @Override
    public String getExplanation() {
        return explanation;
    }
}

Everything you should know about the specific parameters is defined here, and nowhere else (high cohesion)! It forces the developer who is adding new parameters to define the extra properties as required.

I tried to eliminate the boiler plate code, by moving them to a shared abstract enumeration class, but that is not possible. Now we have to copy the same code for every new "ModuleParameter" implementation. Is there a better solution for this?

Module

package com.javaeenotes.modules;


public interface Module {
    void doBusinessStuff();
}

Just the interface of a module, containing a common method.

ExampleModule

package com.javaeenotes.modules;


// Annotate this module with parameter class that contains the
// configurable parameters for this module.
@ParameterClass(parameterClass = ExampleModuleParameter.class)
public class ExampleModule implements Module {

    private String param1;
    private String param2;


    @Override
    public void doBusinessStuff() {
        System.out.println("Hello, I am "
                + this.getClass().getSimpleName()
                + ". I am configured with param1='" + param1
                + "', and param2='" + param2 + "'.");
    }


    public void setParam1(String param1) {
        this.param1 = param1;
    }


    public void setParam2(String param2) {
        this.param2 = param2;
    }
}

This is an example implementation of a module. As you can see, it's just a POJO, making it very easy to write new ones. The class is linked to its parameter class using the annotation placed just before the class definition. The attributes and setters are important here. The names must correspond with the names defined in the parameter class.

ParameterClass

package com.javaeenotes.modules;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


// Only usable on classes.
@Target(ElementType.TYPE)
// Annotation must be available during runtime.
@Retention(RetentionPolicy.RUNTIME)
public @interface ParameterClass {
    Class<? extends ModuleParameter> parameterClass();
}

This is the annotation, which links the module to its parameter class. The factory uses this annotation to find out which configuration values to set.

ModuleType

package com.javaeenotes.modules;


// This is basically a list of supported modules by the factory.
public enum ModuleType {
    EXAMPLE_MODULE(ExampleModule.class);

    private Class<? extends Module> moduleClass;


    private ModuleType(Class<? extends Module> moduleClass) {
        this.moduleClass = moduleClass;
    }


    public Class<? extends Module> getModuleClass() {
        return moduleClass;
    }
}

This enumeration is a list of supported modules. The purpose of this enumeration is to map the name to its class type. This information is required by the module factory to instantiate the correct module class.

ModuleFactory

package com.javaeenotes.modules;

import java.util.Properties;


public interface ModuleFactory {
    Module getModule(String moduleName, Properties configuration)
            throws Exception;
}

The interface of the module factory. It requires the class name, and a "Properties" object containing the configuration of the module.

ModuleFactoryImpl

package com.javaeenotes.modules;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;


public class ModuleFactoryImpl implements ModuleFactory {

    private Map<String, ModuleType> nameToModuleType;


    public ModuleFactoryImpl() {
        // The Map contains all the supported module types.
        nameToModuleType = new HashMap<String, ModuleType>();

        for (ModuleType moduleType : ModuleType.values()) {
            nameToModuleType.put(
                    moduleType.getModuleClass().getSimpleName(),
                    moduleType);
        }
    }


    @Override
    public Module getModule(String moduleName, Properties configuration)
            throws Exception {

        if (nameToModuleType.containsKey(moduleName)) {
            ModuleType moduleType = nameToModuleType.get(moduleName);
            Module module = moduleType.getModuleClass().newInstance();
            configureModule(module, configuration);
            return module;
        }

        throw new IllegalArgumentException(
                "Class not supported: " + moduleName);
    }


    private void configureModule(
            Module module, Properties configuration)
            throws Exception {

        if (module.getClass().isAnnotationPresent(ParameterClass.class)) {
            Map<String, Method> methodNameToMethod =
                    getMethodNameToMethod(module);

            for (ModuleParameter param : getModuleParameters(module)) {
                String configValue = configuration.getProperty(
                        param.toString());

                // Set default value if configuration value is empty.
                if (configValue.isEmpty()) {
                    configValue = param.getDefaultValue();
                }

                // Check if value is required.
                if ((configValue == null || configValue.isEmpty())
                        && param.isRequired()) {

                    throw new IllegalArgumentException(
                            "Configuration value missing for: "
                            + param.toString());
                }

                // Set configuration value in module.
                invokeParameterSetter(methodNameToMethod, module,
                        param.toString(), configValue);
            }
        }
    }


    private Map<String, Method> getMethodNameToMethod(Module module) {
        Map<String, Method> methodNameToMethod =
                new HashMap<String, Method>();

        for (Method method : module.getClass().getMethods()) {
            methodNameToMethod.put(method.getName(), method);
        }

        return methodNameToMethod;
    }


    private ModuleParameter[] getModuleParameters(Module module) {
        return module.getClass()
                .getAnnotation(ParameterClass.class)
                .parameterClass().getEnumConstants();
    }


    private void invokeParameterSetter(
            Map<String, Method> methodNameToMethod,
            Module module, String paramName, String configValue)
            throws Exception {

        String setterMethodName = getSetterMethodName(paramName);

        if (methodNameToMethod.containsKey(setterMethodName)) {
            methodNameToMethod.get(setterMethodName).invoke(
                    module, configValue);
        } else {
            throw new IllegalArgumentException(
                    "No setter found for: " + paramName);
        }
    }


    private String getSetterMethodName(String paramName) {
        return "set" + Character.toUpperCase(paramName.charAt(0))
                + paramName.substring(1);
    }
}

Using the name of the module class, this factory instantiates the correct class with the help of the "ModuleType" enumeration class. Then it tries to find out if this module requires configuration, by detecting the annotation. The annotation specifies the use of the "ExampleModuleParameter" enumeration for configuration. Using the enumeration, and the "Properties" object, it calls the setters with the configuration values. It also checks if a required configuration value is present. The result is a module with all required configuration values set.

A drawback is that the method name of the setters is restricted. It must be the same as the name of the parameter (excluding the "set" string), which is defined in the enumeration. A solution for this is to use annotations in the setter method definition to specify which configuration parameter it corresponds to. Using string literals to tie things together makes the code brittle.

Main

package com.javaeenotes.modules;

import java.util.Properties;


public class Main {
    public static void main(String[] args) {
        // Build the configuration properties.
        Properties configuration = new Properties();
        configuration.setProperty(
                ExampleModuleParameter.PARAM1.toString(), "");
        configuration.setProperty(
                ExampleModuleParameter.PARAM2.toString(), "param2");

        try {
            // Get instance of configured module from factory.
            Module module = new ModuleFactoryImpl()
                .getModule("ExampleModule", configuration);

            // Run the module.
            module.doBusinessStuff();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

This class demonstrates the whole solution. If run, it prints: Hello, I am ExampleModule. I am configured with param1='DEFAULT', and param2='param2'.

Conclusion

As you can see, writing new modules is very easy by implementing just the two interfaces. All the configuration code is centralized in the factory implementation.

If you have ideas for improvements or better solutions, please don't hesitate to share them in the comments.

Monday, March 5, 2012

Quick introduction to OpenSSL

OpenSSL is an open source Secure Sockets Layer (SSL v2/v3) and Transport Layer Security (TLS v1) implementation. I exclusively use this for handling SSL related tasks, and diagnostics. I will explain the OpenSSL commands I frequently use in this blog post. Before we go to the commands, let's do a quick explanation of the terminology used in SSL:
  • Certificate Signing Request (CSR): a message format for a certificate signing request for a Certificate Authority. This message is required for generating/requesting certificates.
  • Public/Private key: a private key should be kept secret by the owner, and can be used to sign data, or decrypt messages encrypted with the public key. The public key is used to encrypt message which are only to be read by the owner of the private key. The encryption/decryption process tends to be slow compared to using a symmetric encryption key.
  • Symmetric key: one key for encrypting and decrypting. This process is fast. When we use SSL in our browser, the public/private key is only used to negotiate a symmetric key encryption method. The actual data transfer encryption is therefore symmetric (fast).
  • (Root) Certificate: a certificate contains identification information, a public key that can be used to encrypt messages, and a signature of a Certificate Authority. The signature tells us that the identification information is trusted/verified by the Certificate Authority. If we trust the Certificate Authority, then we also trust the identity of the owner of the certificate (authentication).
  • Certificate Authority (CA): an organization we consider to be trusted. A CA will sign and issue certificates. Using the root certificate of a CA, we can verify if other certificates are indeed signed by this particular CA. Your browser has a standard set of root certificates of commonly recognized CA's.

Common commands

To generate a CSR for a certificate with a 2048-bit private key:
openssl req -out certificate.csr -new
  -newkey rsa:2048 -nodes -keyout private.key
Generate a self-signed certificate (we are our own CA):
openssl req -x509 -nodes -days 365 -newkey rsa:2048
  -keyout private.key -out certificate.crt
Print out the information contained in a certificate, such as identification, and the public key:
openssl x509 -in certificate.crt -text -noout
To check a web server for its SSL-certificates:
openssl s_client -showcerts -connect google.com:443 </dev/null

References:

Monday, December 26, 2011

Annotated callback methods in custom class

We're implementing a simple message service framework to demonstrate how we can implement a framework that uses annotations, like JAX-WS. Using annotations minimizes boilerplate code, and promotes high cohesion of classes. It also promotes decoupling, because it prevents forcing the use of framework classes on clients as much as possible.

The messaging framework has one interface with a register method, which is used to register custom objects that handle incoming messages. The framework does not know what the type is of this object. It uses the reflection API to check for annotated methods, which tell the messaging framework to call these methods when a message has to be handled.

The message service framework supports two types of messages, which we define in as an enum type in MessageType.java:

package com.javaeenotes;


public enum MessageType {
    NORMAL,
    URGENT
}

The interface of the message service framework has a method to register custom client objects that will handle incoming messages. The second method of the interface is only included, so we can send messages through this framework. The interface of the message service framework is defined in MessageService.java:

package com.javaeenotes;


public interface MessageService {
    void registerMessageHandler(Object object);

    void sendMessage(String message, MessageType messageType)
        throws Exception;
}

Now, we define our annotation. This annotation can be used by the client application to mark methods that will handle incoming messages generated by the message service framework. It also has a message type enum parameter, so the framework will only call this method if the type of the message matches the specified type parameter. The annotation is defined in MessageMethod.java:

package com.javaeenotes;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


// Only usable on methods.
@Target(ElementType.METHOD)
// Annotation must be available during runtime.
@Retention(RetentionPolicy.RUNTIME)
public @interface MessageMethod {
    MessageType messageType() default MessageType.NORMAL;
}

A client object will use the annotation to mark callback methods for specified message types. The example client handler has one method for both message types. An example client message handler class for this demo is defined as ExampleMessageHandler.java:

package com.javaeenotes;


public class ExampleMessageHandler {
    @MessageMethod(messageType = MessageType.NORMAL)
    public void handleNormalMessage(String message) {
        System.out.print("NORMAL MESSAGE: " + message + "\n");
    }


    @MessageMethod(messageType = MessageType.URGENT)
    public void handleUrgentMessage(String message) {
        System.out.println("URGENT MESSAGE: " + message + "\n");
    }
}

The most important class of this tutorial is: MessageServiceImpl.java, which you can find below. This is the implementation of our message service framework interface. This class is responsible for determining callback methods provided by the client, and call them when messages arrive.

package com.javaeenotes;


import java.lang.reflect.Method;


public class MessageServiceImpl implements MessageService {
    private Object messageHandler;


    @Override
    public void registerMessageHandler(Object messageHandlerObject) {
        messageHandler = messageHandlerObject;
    }


    @Override
    public void sendMessage(String message, MessageType messageType)
            throws Exception {
        
        for (Method method : messageHandler.getClass().getMethods()) {
            if (method.isAnnotationPresent(MessageMethod.class)) {
                MessageMethod messageMethod
                    = method.getAnnotation(MessageMethod.class);

                if (messageMethod.messageType() == MessageType.NORMAL
                        && messageType == MessageType.NORMAL) {

                    method.invoke(messageHandler, message);
                } else if (messageMethod.messageType()
                        == MessageType.URGENT
                        && messageType == MessageType.URGENT) {

                    method.invoke(messageHandler, message);
                }
            }
        }
    }
}

Now, we only need a Main class to get this demonstration working. The class will create the example client object, and register it in the framework. Then, it will create two example messages, and pass them to the framework, which then will call the annotated callback methods of the example client object. The Main class is defined in: Main.java.

package com.javaeenotes;


public class Main {
    public static void main(String[] args) {
        // Create message service and register message handler.
        MessageService messageService = new MessageServiceImpl();
        messageService.registerMessageHandler(
                new ExampleMessageHandler());

        try {
            // Sending test messages through the message service.
            messageService.sendMessage("This is a normal message.",
                    MessageType.NORMAL);

            messageService.sendMessage("This is an urgent message!",
                    MessageType.URGENT);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

If we run the Main class, we should get the following output:

NORMAL MESSAGE: This is a normal message.
URGENT MESSAGE: This is an urgent message!

Tuesday, December 20, 2011

Custom SLF4J Logger adapter

The Simple Logging Facade for Java or (SLF4J) is a simple facade for various logging frameworks. It decouples the logging framework from the application. The deployer can choose which logging framework to use. For every popular logging framework, an adapter Jar file is available. Deploy this file together with the core SLF4J Jar file, and the logging automagically works.

In my current work situation, a financial application I developed has to be integrated into an existing server application. This server application uses its own logging framework. Luckily, I've been using SLF4J, so logging integration is just a matter of writing a custom SLF4J adapter and replace the current adapter (SLF4J-Log4J) with it.

Writing an adapter is very easy. You only need to create three files:

  1. Create an implementation of the logger interface: org.slf4j.Logger
  2. Create a factory for the new logger using this interface: org.slf4j.ILoggerFactory
  3. Create your own binder class, so SLF4J knows which factory to use to get loggers: org.slf4j.spi.LoggerFactoryBinder
It is important to create these files in the package: org.slf4j.impl, because SLF4J is using this to find the correct adapter.

Firstly, we create the class that implements the SLF4J Logger interface. The implementation of this interface is used by SLF4J to delegate the logging. It's an interface with a large number of methods, so let the IDE generate the empty methods for you. Next is to implement the bodies of the methods to do the actual logging. In my case, I call the custom logging framework.

package org.slf4j.impl;

import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.helpers.MessageFormatter;

public class MyLoggerAdapter implements Logger {
    // Bunch of inherited methods here. Let your IDE generate this.
    // Implement these methods to do your own logging.
}

Now, we create a factory implementation for the Logger class. This class is used to retrieve Logger objects used by the application. Make sure it implements the ILoggerFactory interface.

package org.slf4j.impl;

import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;

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


public class MyLoggerFactory implements ILoggerFactory {
    private Map<String, MyLoggerAdapter> loggerMap;

    public MyLoggerFactory() {
        loggerMap = new HashMap<String, MyLoggerAdapter>();
    }

    @Override
    public Logger getLogger(String name) {
        synchronized (loggerMap) {
            if (!loggerMap.containsKey(name)) {
                loggerMap.put(name, new MyLoggerAdapter(name));
            }

            return loggerMap.get(name);
        }
    }
}

Finally, we need a way to register or bind the logger factory to SLF4J. To do that, we have to customize the StaticLoggerBinder class. You can almost use the same code provided by other adapters. I shamelessly ripped my code from the Log4J adapter.

package org.slf4j.impl;


import org.slf4j.ILoggerFactory;
import org.slf4j.spi.LoggerFactoryBinder;


public class StaticLoggerBinder implements LoggerFactoryBinder {

    /**
     * The unique instance of this class.
     */
    private static final StaticLoggerBinder SINGLETON
        = new StaticLoggerBinder();

    /**
     * Return the singleton of this class.
     *
     * @return the StaticLoggerBinder singleton
     */
    public static final StaticLoggerBinder getSingleton() {
        return SINGLETON;
    }


    /**
     * Declare the version of the SLF4J API this implementation is
     * compiled against. The value of this field is usually modified
     * with each release.
     */
    // To avoid constant folding by the compiler,
    // this field must *not* be final
    public static String REQUESTED_API_VERSION = "1.6";  // !final

    private static final String loggerFactoryClassStr
        = MyLoggerFactory.class.getName();

    /**
     * The ILoggerFactory instance returned by the
     * {@link #getLoggerFactory} method should always be the same
     * object.
     */
    private final ILoggerFactory loggerFactory;

    private StaticLoggerBinder() {
        loggerFactory = new MyLoggerFactory();
    }

    public ILoggerFactory getLoggerFactory() {
        return loggerFactory;
    }

    public String getLoggerFactoryClassStr() {
        return loggerFactoryClassStr;
    }
}

Compile and deploy this code together with the SLF4J jar, and logging will be handled by the our new adapter.

Links:

Friday, December 16, 2011

Running Wicket on Jetty

If you're only using the web functionality provided by an application server, or you want to optimize HTTP performance, it would be a good idea to consider embedding the Jetty HTTP server inside your application. This way, you don't even have to generate a WAR-file and a web.xml file. The application is just a basic Java application, which you can run on any server with a Java VM.

Maven setup

If you're using a Maven project, add the following dependencies to the pom-file in the "dependencies"-element in order to start working right away:

<dependency>
  <groupId>org.mortbay.jetty</groupId>
  <artifactId>jetty-hightide</artifactId>
  <version>8.0.4.v20111024</version>
</dependency>

<dependency>
  <groupId>org.apache.wicket</groupId>
  <artifactId>wicket-core</artifactId>
  <version>1.5.3</version>
</dependency>

Minimal Wicket application

For our example, we use a minimal Wicket application that consists of one page.

The ExampleWicketApplication.java file:
package com.javaeenotes;


import org.apache.wicket.Page;
import org.apache.wicket.protocol.http.WebApplication;


public class ExampleWicketApplication extends WebApplication {

    @Override
    public Class<? extends Page> getHomePage() {
        return HomePage.class;
    }
}
The HomePage.java file:
package com.javaeenotes;


import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;


public class HomePage extends WebPage {
    public HomePage() {
        add(new Label("message", "Hello World!"));
    }
}
The HomePage.html file:
<html>
<body>
  <span wicket:id="message"></span>
</body>
</html>

The final class we need is a main class with the static main-method, in which we tie the Wicket servlet to the Jetty server.

The Main.java file:
package com.javaeenotes;


import org.apache.wicket.protocol.http.WicketFilter;
import org.apache.wicket.protocol.http.WicketServlet;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.bio.SocketConnector;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.webapp.WebAppContext;


public class Main {
    public static void main(String[] args) {
        // Object that holds, and configures the WicketServlet.
        ServletHolder servletHolder =
                new ServletHolder(new WicketServlet());
        servletHolder.setInitParameter(
                "applicationClassName",
                "com.javaeenotes.ExampleWicketApplication");
        servletHolder.setInitParameter(
                WicketFilter.FILTER_MAPPING_PARAM, "/*");
        servletHolder.setInitOrder(1);

        // Web context configuration.
        WebAppContext context = new WebAppContext();
        context.addServlet(servletHolder, "/*");
        context.setResourceBase("."); // Web root directory.

        // The HTTP-server on port 8080.
        Server server = new Server();
        SocketConnector connector = new SocketConnector();
        connector.setPort(8080);
        server.setConnectors(new Connector[]{connector});
        server.setHandler(context);

        try {
            // Start HTTP-server.
            server.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Run the main method in a VM, and connect to port 8080 with a browser to check that the application is working.

Friday, October 28, 2011

MyBatis and Oracle Database

While I was using MyBatis with the Oracle Database, I ran into two problems:

  1. Generating primary keys when inserting database rows
  2. Mapping Java boolean type to a database type
The next sections describe how I solved these problems.

Inserting new rows

Unlike other databases like MySQL, the Oracle database does not provide the functionality to generate primary keys automatically when inserting rows. Normally, I would create a sequence together with a trigger in the Oracle database. The sequence is a thread-safe number generator, and the trigger is custom code that is called on certain type of events. In this case an "insert" event, which will update the row with the primary key.

With MyBatis, we still need the sequence. The trigger functionality is provided by MyBatis. Let's get started by creating an example class in Java. I left the "getters" and "setters" out, but we still need them in the actual code ofcouse.

public class Client {
    private Integer id;
    private String name;

    // Setters and Getters for the attributes here.
}

Next, we have to prepare the database by create a table that corresponds with the "Client" class, and a sequence.

CREATE TABLE client (
    id INTEGER primary key,
    name VARCHAR2(100)
);

CREATE SEQUENCE client_seq
    START WITH 1
    INCREMENT BY 1
    NOCACHE
    NOCYCLE;

MyBatis uses "mapper" interfaces (defined and provided by us) to map the class to actual SQL-statements. Additional configuration can be provided by either XML, or annotations. When the configuration is simple, I prefer using annotations. Complex configurations are often only possible by using XML. The following mapper interface provides mapping for inserting the "Client" class. The interface is only used by MyBatis, we don't have to provide an actual implementation of it.

public interface ClientMapper {
  @Insert("INSERT INTO client (id, name) VALUES (#{id}, #{name})")
  @SelectKey(
            keyProperty = "id",
            before = true,
            resultType = Integer.class,
            statement = { "SELECT client_seq.nextval AS id FROM dual" })
  public Integer insertClient(Client client);
}

The "@SelectKey" annotation tells MyBatis to get the primary key value from the sequence, and use it as primary key when executing the insert statement.

Mapping boolean types

The final example illustrates how we deal with boolean types when using MyBatis. We extend the "Client" class with a boolean attribute "admin".

public class Client {
    private Integer id;
    private String name;
    private boolean admin;

    // Setters and Getters for the attributes here.
}

Add a "VARCHAR(1)" column that acts like a boolean attribute to the corresponding "Client" database table. The column value "Y" corresponds to boolean value true, and "N" to boolean false.

CREATE TABLE client (
    id INTEGER primary key,
    name VARCHAR2(100),
    admin varchar(1)
);

MyBatis doesn't know how the Java boolean is mapped to a database type, as there's no primitive database boolean type. We have to provide MyBatis with a converter, which implements the "TypeHandler" interface. This converter will then be used to convert a Java boolean to a database type, and vice versa.

package com.javaeenotes;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

public class BooleanTypeHandler implements TypeHandler {

    private static final String YES = "Y";
    private static final String NO = "N";


    @Override
    public Boolean getResult(ResultSet resultSet, String name)
            throws SQLException {

        return valueOf(resultSet.getString(name));
    }


    @Override
    public Boolean getResult(CallableStatement statement, int position)
            throws SQLException {

        return valueOf(statement.getString(position));
    }


    @Override
    public void setParameter(
            PreparedStatement statement,
            int position,
            Boolean value,
            JdbcType jdbcType) throws SQLException {

        statement.setString(position, value.booleanValue() ? YES : NO);
    }


    private Boolean valueOf(String value) throws SQLException {

        if (YES.equalsIgnoreCase(value)) {
            return new Boolean(true);
        } else if (NO.equalsIgnoreCase(value)) {
            return new Boolean(false);
        } else {
            throw new SQLException("Unexpected value "
                    + value
                    + " found where "
                    + YES
                    + " or "
                    + NO
                    + " was expected.");
        }
    }
}

Now, configure MyBatis to use the type handler for booleans in the mapper interface where needed.


    @Insert("INSERT INTO client (id, name, admin) VALUES (#{id}, #{name}, #{admin,typeHandler=com.javaeenotes.BooleanTypeHandler})")
    @SelectKey(
            keyProperty = "id",
            before = true,
            resultType = Integer.class,
            statement = { "SELECT client_seq.nextval AS id FROM dual" })
    public Integer insertClient(Client client);

    @Select("SELECT * FROM client WHERE id = #{clientId}")
    @Results(
            value = {
                @Result(
                    property = "id",
                    column = "id"),

                @Result(
                    property = "name",
                    column = "name"),

                @Result(
                    property = "admin",
                    column = "admin",
                    typeHandler = BooleanTypeHandler.class)
             })
    public Client getClient(int clientId);

    @Update("UPDATE client SET name = #{name}, admin = #{admin,typeHandler=com.javaeenotes.BooleanTypeHandler} WHERE id = #{id}")
    public void updateClient(Client client);

Thursday, October 13, 2011

Adding Oracle JDBC driver to Maven 2 repository

I like my library JAR-files to be managed by Maven 2. It makes life much easier. Sadly, due to the binary license there is no public Maven 2 repository with the Oracle JDBC driver. So we have to add the JDBC driver manually to our local Maven 2 repository.

First, download the latest Oracle JDBC driver here: http://www.oracle.com/technetwork/indexes/downloads/index.html. Do a search on "JDBC" to find the correct download link.

If you're using Maven inside Eclipse like I am, the "mvn" command will not work from the command prompt. We have to install Maven 2 on our system. You can download Maven 2 here: http://maven.apache.org/download.html. Simply unzip, and make sure the "bin" directory is in your command path.

Now, we can install the JAR file with the following command:

mvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc6
  -Dversion=11.2.0.3 -Dpackaging=jar -Dfile=ojdbc6.jar -DgeneratePom=true
Be sure to check the parameters, like the version number.

Finally, we can use the Oracle JDBC driver in our Maven 2 project by adding the following dependency to our "pom.xml" file:

<dependency>
    <groupId>com.oracle</groupId>
    <artifactId>ojdbc6</artifactId>
    <version>11.2.0.3</version>
</dependency>