encrypting ibatis’ sqlmapconfig.xml

after im writing this article, i keep wondering “how can i keep database’s password secure.?”. As you all know, i keep database’s password plain at SqlMapConfig.xml. Therefore, we need to keep our database’s password secure. One of the simplest method is to encrypting SqlMapConfig’s properties.

Im using Swing, jasypt library for basic encryption, and iBatis 2.3.4. Jasypt also need 2 additional jars, commons-lang and commons-codec, you can find them at apache’s website.

first i create a properties file, named db.properties

JDBC.Driver=1rzI2NrjkRaiwdZso6qZaI0THnqKx/wkAROxbfaCL/E=
JDBC.ConnectionURL=7EpsURgD/FFzdzuDTKYtdcT3iGPePc8uklqBweCnbCkw1wjUAKPyEA==
JDBC.Username=ciUNsgpnvS6bEkkB1F/Q8g==
JDBC.Password=c5dvo6UUKK5t633Dt6lvma0WAm5snxb+

after that, i create a singleton class to do all the Encryption-Decryption functions

package com.edw.util;

import org.jasypt.util.text.BasicTextEncryptor;

/**
 * @author edw
 */
public class BasicEncryption {

    private static final BasicEncryption basicEncryption = new BasicEncryption();
    private final String CONSTANT = "busuk";

    private BasicTextEncryptor textEncryptor = new BasicTextEncryptor();

    private BasicEncryption(){        
        textEncryptor.setPassword(CONSTANT);
    }

    public static BasicEncryption getInstance(){
        return basicEncryption;
    }

    public String encrypt(String word){
        return textEncryptor.encrypt(word);
    }

    public String decrypt(String word){
        return textEncryptor.decrypt(word);
    }

}

after that, we modified SqlMapConfig.java to put decrypted properties into SqlMapConfig.xml

package com.edw.config;

import com.edw.util.BasicEncryption;

import com.ibatis.common.resources.Resources;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;
import java.io.File;
import java.io.FileInputStream;

import java.io.IOException;
import java.io.Reader;
import java.util.Properties;

/**
 *
 * @author edw
 */
public class SqlMapConfig {

    protected static final SqlMapClient sqlMap;

    static {
        try {

            File file = new File("db.properties");
            FileInputStream fileInputStream = new FileInputStream(file);
            Properties properties = new Properties();            
            properties.load(fileInputStream);
            fileInputStream.close();

            // load encryption class
            BasicEncryption basicEncryption = BasicEncryption.getInstance();

            properties.setProperty("JDBC.Driver", basicEncryption.decrypt(properties.getProperty("JDBC.Driver")));
            properties.setProperty("JDBC.ConnectionURL", basicEncryption.decrypt(properties.getProperty("JDBC.ConnectionURL")));
            properties.setProperty("JDBC.Username", basicEncryption.decrypt(properties.getProperty("JDBC.Username")));
            properties.setProperty("JDBC.Password", basicEncryption.decrypt(properties.getProperty("JDBC.Password")));

            Reader reader = Resources.getResourceAsReader("com/edw/sqlmap/sqlmapconfig.xml");
            sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader, properties);
        } catch (IOException e) {
            throw new RuntimeException("Fatal Error, ga dapet sqlmapconfignya.  Cause: " + e, e);
        } catch (Exception e){
            throw new RuntimeException("Fatal Error.  Cause: " + e, e);
        }
    }

    public static SqlMapClient getSqlMap() {
        return sqlMap;
    }
}

and we set the variable at sqlmapconfig.xml to fit decrypted properties values.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMapConfig
PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">

<sqlMapConfig>
    <settings
        useStatementNamespaces="true"
        lazyLoadingEnabled="true"
        enhancementEnabled="true"
        maxSessions="20"
        />

    <transactionManager type="JDBC" commitRequired="false">
        <dataSource type="SIMPLE">

            <property name="SetAutoCommitAllowed" value="false"/>
            <property name="DefaultAutoCommit" value="false"/>
            
            <property name="JDBC.Driver" value="${JDBC.Driver}"/>
            <property name="JDBC.ConnectionURL" value="${JDBC.ConnectionURL}"/>
            <property name="JDBC.Username" value="${JDBC.Username}"/>
            <property name="JDBC.Password" value="${JDBC.Password}"/>
   
        </dataSource>
    </transactionManager>


    <!-- dont forget to register your sql map configs -->
    <sqlMap resource="com/edw/sqlmap/contoh.xml"/>


</sqlMapConfig>

this is my project structure
structure
structure2

this is what will happen if we submit the form
success

you can check it in the database
database

InvalidClassException: com.sun.rowset.providers. RIOptimisticProvider; local class incompatible: stream classdesc serialVersionUID

i ran into this weird exception when im trying to connecting a Swing Application + Crystal Report to a database via EJB. I dont know why, but this error always happen only at reporting module. And what makes it weird that this exception only happen at several terminals only.

java.rmi.RemoteException: null; nested exception is: 
	java.io.InvalidClassException: com.sun.rowset.providers.RIOptimisticProvider; local class incompatible: stream classdesc serialVersionUID = -3143367176751761936, local class serialVersionUID = -8429279896237029122
	at com.rubean.statelesstunnel.client.BusinessInterfaceProxyFactory$Handler.invoke(Unknown Source)
	at $Proxy7.runNamedReportQuery(Unknown Source)
	at com.xxx.GenericQueryDelegate.runnamedReportQuery(ReportQueryDelegate.java:166)
	at com.xxx.report.RptOne.setQuery(RptOne.java:78)
	at com.xxx.report.RptOne$runReport.run(RptOne.java:136)
Caused by: java.io.InvalidClassException: com.sun.rowset.providers.RIOptimisticProvider; local class incompatible: stream classdesc serialVersionUID = -3143367176751761936, local class serialVersionUID = -8429279896237029122
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:562)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1583)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1496)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1732)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
	at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1947)
	at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1871)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1753)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
	at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1947)
	at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1871)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1753)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
	at com.rubean.statelesstunnel.client.http.HttpRequestorCompression.postRequest(HttpRequestorCompression.java:60)
	... 5 more
	

after some research i found out that this exception happen only to terminals using JRE 1.60 update 7 or less.

C:\Users\edw>java -version
java version "1.6.0_07"
Java(TM) SE Runtime Environment (build 1.6.0_07-b06)
Java HotSpot(TM) Client VM (build 10.0-b23, mixed mode, sharing)

this exception gone after i updated every terminal’s JRE into JRE 1.60 update 19

C:\Users\edw>java -version
java version "1.6.0_19"
Java(TM) SE Runtime Environment (build 1.6.0_19-b04)
Java HotSpot(TM) Client VM (build 16.2-b04, mixed mode, sharing)

And now i have to update every terminals that are still using JRE 1.6.0_07. Weird eh, hahahaa…..

Connecting Remote EJB3 from a Servlet with WebLogic 10.3

Right now i’m trying to connect 2 different applications that located in a different server. Im using remote EJB3 to connecting them, with Apache Tomcat as server 1 and WebLogic 10.3 as server 2. Im using Netbeans 6.8 as primary IDE for this test.

create an EJB Module Project from Netbeans IDE, and create these files

first is the remote interface

package com.edw.ejb3;

import javax.ejb.Remote;

@Remote
public interface HelloEJBRemote {
    String sayHello(final String name);    
}

and the ejb implementation

package com.edw.ejb3;

import javax.ejb.Stateless;

@Stateless(mappedName="HelloEJB")
public class HelloEJB implements HelloEJBRemote {
    public String sayHello(final String name) {
        return "Hello "+name+" how do you do?";
    }
}

Package it as JAR or EAR, start Weblogic then use Admin Console (default: http://localhost:7001/ ) to deploy the EJB.
deployment

and to make sure, you can check EJB’s JNDI
ejb's jndi

next is creating a Web Project, dont forget to include your EJB project to your Web Project’s library.

create a servlet file to perform a connection to Remote EJBs.

package ejb;

import com.edw.ejb3.HelloEJBRemote;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class EJBServlet extends HttpServlet {

    @Override
   protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        try {

            Hashtable<String, String> env = new Hashtable<String, String>();
            env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
            env.put(Context.PROVIDER_URL, "t3://localhost:7001");

            Context ic = new InitialContext(env);

            HelloEJBRemote remote = (HelloEJBRemote) ic.lookup("HelloEJB#com.edw.ejb3.HelloEJBRemote");

            out.println(remote.sayHello("Edwin"));

        } catch (Exception ex) {
            out.print(ex);
            ex.printStackTrace();
        } finally {
            out.close();
        }
    } 
}

and this is my web.xml configuration file

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <servlet>
        <servlet-name>EJBServlet</servlet-name>
        <servlet-class>ejb.EJBServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>EJBServlet</servlet-name>
        <url-pattern>/ejb</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

if you find this error,

javax.naming.NoInitialContextException: Cannot instantiate class: weblogic.jndi.WLInitialContextFactory [Root exception is java.lang.ClassNotFoundException: weblogic.jndi.WLInitialContextFactory]

it happen because you havent put wlfullclient.jar (WebLogic library) into your Apache Tomcat’s library folder. You can find it in your Weblogic installation folder ({weblogic}\server\lib) or you can create it your self, by executing wljarbuilder.jar.

java -jar wljarbuilder.jar

tomcat libs

you can test your app by running your web project from your IDE.
web content

this is my netbeans project structure
Netbeans Project Structure

A Simple Swing and iBatis Integration Example

According to Wikipedia, iBATIS is a persistence framework which automates the mapping between SQL databases and objects in Java, .NET, and Ruby on Rails. In Java, the objects are POJOs (Plain Old Java Objects). The mappings are decoupled from the application logic by packaging the SQL statements in XML configuration files. The result is a significant reduction in the amount of code that a developer needs to access a relational database using lower level APIs like JDBC and ODBC.

Here, we are trying to create a simple java application using iBatis framework. Im using Netbeans as my IDE and MySql for my database. First of all, we create a simple “test” database, and “contoh” table consisting of two varchar fields.

CREATE DATABASE 'test'
USE 'test'

CREATE TABLE 'contoh' (
  'nama' varchar(30) NOT NULL,
  'alamat' varchar(100) DEFAULT NULL,
  PRIMARY KEY ('nama')
) 

next step is creating a java bean for database mapping

package com.edw.bean;

public class Contoh {

    private String nama;
    private String alamat;

    public String getAlamat() {
        return alamat;
    }

    public void setAlamat(String alamat) {
        this.alamat = alamat;
    }

    public String getNama() {
        return nama;
    }

    public void setNama(String nama) {
        this.nama = nama;
    }
    
}

and an xml mapping for database queries, i name it contoh.xml, and place it in

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd" >
<sqlMap namespace="contoh" >

  <insert id="insertContoh" parameterClass="com.edw.bean.Contoh" >
    insert into contoh (nama, alamat)
    values (#nama:VARCHAR#, #alamat:VARCHAR#)
  </insert>
</sqlMap>

and one xml file to contain all of our basic database connection and configuration files

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMapConfig
PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">

<sqlMapConfig>
    <settings
        useStatementNamespaces="true"
        lazyLoadingEnabled="true"
        enhancementEnabled="true"
        maxSessions="20"
        />

    <transactionManager type="JDBC" commitRequired="false">
        <dataSource type="SIMPLE">

            <property name="SetAutoCommitAllowed" value="false"/>
            <property name="DefaultAutoCommit" value="false"/>
            
            <property name="JDBC.Driver" value="com.mysql.jdbc.Driver"/>
            <property name="JDBC.ConnectionURL" value="jdbc:mysql://localhost/test"/>
            <property name="JDBC.Username" value="root"/>
            <property name="JDBC.Password" value=""/>
   
        </dataSource>
    </transactionManager>


    <!-- dont forget to register your sql map configs -->
    <sqlMap resource="com/edw/sqlmap/contoh.xml"/>

</sqlMapConfig>

dont forget to register you xml queries here (line 28), or iBatis wont find it. And as you can see, at line 11, we can set our maximum session to database. 20 maxSessions means, there wont be more than 20 concurrent connection to database. Dont worry, iBatis can also connect to your connection pooling or JNDI as Datasource.

next step is, we create a singleton java class, to load our iBatis configuration.

package com.edw.config;

import com.ibatis.common.resources.Resources;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;

import java.io.IOException;
import java.io.Reader;

public class SqlMapConfig {

    protected static final SqlMapClient sqlMap;

    static {
        try {
            Reader reader = Resources.getResourceAsReader("com/edw/sqlmap/sqlmapconfig.xml");
            sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
        } catch (IOException e) {
            throw new RuntimeException("Fatal Error, ga dapet sqlmapconfignya.  Cause: " + e, e);
        } catch (Exception e){
            throw new RuntimeException("Fatal Error.  Cause: " + e, e);
        }
    }

    public static SqlMapClient getSqlMap() {
        return sqlMap;
    }
}

and after that, we create our UI Class. Im using a simple Swing class for example.

package com.edw.ui;

import com.edw.bean.Contoh;
import com.edw.config.SqlMapConfig;
import com.ibatis.sqlmap.client.SqlMapClient;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.SQLException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

public class FrameUtama extends JFrame implements ActionListener {

    private JTextField txtNama = new JTextField();
    private JTextField txtAlamat = new JTextField();
    private JButton cmdButton = new JButton("Save");

    public FrameUtama(){
        setLayout(new GridLayout(3, 3));
        Container con = this.getContentPane();
        con.add(new JLabel("nama : "));
        con.add(txtNama);
        con.add(new JLabel("Alamat : "));
        con.add(txtAlamat);
        con.add(cmdButton);

        cmdButton.addActionListener(this);
    }

    public void actionPerformed(ActionEvent e) {

    }

    public static void main(String[] edw) {
        FrameUtama frameUtama = new FrameUtama();
        frameUtama.setVisible(true);
        frameUtama.setSize(300,150);
        frameUtama.setLocationRelativeTo(null);
    }

}

and we just put this simple snippet code to connect our Swing UI code to database via iBatis.

 public void actionPerformed(ActionEvent e) {
        if(e.getSource() == cmdButton){
            Contoh contoh = new Contoh();
            contoh.setNama(txtNama.getText());
            contoh.setAlamat(txtAlamat.getText());

            SqlMapClient sqlMapClient = SqlMapConfig.getSqlMap();
            try {
                sqlMapClient.insert("contoh.insertContoh", contoh);
                System.out.println("Success");
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }
    }

well, this is the project file structure in NB 6.8.
my ibatis project structure

and this is the UI layout
Application GUI

this is what happen if we submit a data
successfully submit data

the data we submitted is in the database
mysql data

dont forget to download ibatis jars here, im using iBatis 2.3.4 currently.
Thanks.

Random Date Generator dengan Java

Iseng-iseng bikin class sederhana untuk menghasilkan suatu tanggal random yang terletak antara tanggalSebelum (Date) dan tanggalSesudah (Date). Rencananya ini mo gw pakek bwt testing aplikasi. Sorry klo classnya rada berantakan, bikinnya juga buru-buru, sembari nunggu adzan Maghrib.

package com.edw.random;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * 
 * @author edwin
 */
public class RandomDateGenerator extends Thread {

	private Date sebelum;
	private Date sesudah;
	private int repetition;

	public Date getSebelum() {
		return sebelum;
	}

	public void setSebelum(Date sebelum) {
		this.sebelum = sebelum;
	}

	public Date getSesudah() {
		return sesudah;
	}

	public void setSesudah(Date sesudah) {
		this.sesudah = sesudah;
	}

	public int getRepetition() {
		return repetition;
	}

	public void setRepetition(int repetition) {
		this.repetition = repetition;
	}

	public RandomDateGenerator() {
	}

	@Override
	public void run() {
		try {
			for (int i = 0; i < repetition; i++) {
				Thread.sleep(1000);
				System.out.println(getRandomDateBetween(sebelum, sesudah));
			}
		} catch (InterruptedException interruptedException) {
			interruptedException.printStackTrace();
		}
	}

	private synchronized Date getRandomDateBetween(Date from, Date to) {
		Calendar cal = Calendar.getInstance();

		cal.setTime(from);
		BigDecimal decFrom = new BigDecimal(cal.getTimeInMillis());

		cal.setTime(to);
		BigDecimal decTo = new BigDecimal(cal.getTimeInMillis());

		BigDecimal selisih = decTo.subtract(decFrom);
		BigDecimal factor = selisih.multiply(new BigDecimal(Math.random()));

		return new Date((factor.add(decFrom)).longValue());
	}

	public static void main(String[] args) throws Exception {
		RandomDateGenerator rdg = new RandomDateGenerator();
		rdg.setSebelum(new SimpleDateFormat("dd MM yyyy").parse("17 08 1978"));
		rdg.setSesudah(new SimpleDateFormat("dd MM yyyy").parse("17 08 1990"));
		rdg.setRepetition(10);
		rdg.start();
	}
}

may the SOURCE be with you.

Wassalam.