Last Modified 2020.4.10

Modularization - JavaBank

This article covers modularization of the JDBC Example JavaBank.
Download the JavaBank source with the following command.

git clone https://github.com/kimjonghoon/JavaBank

Download the Oracle JDBC Driver from https://www.oracle.com/database/technologies/jdbcdriver-ucp-downloads.html#license-lightbox.
Download the latest slf4j-api and slf4j-simple from http://www.slf4j.org/download.html.
Copy ojdbc6.jar, slf4j-api-1.7.30.jar and slf4j-simple-1.7.30.jar to lib/ directory.

src/
├── net
│   └── java_school
│       └── bank
│            ├── Account.java
│            ├── Bank.java
│            ├── BankDao.java
│            ├── BankUi.java
│            ├── ShinhanBank.java
│            ├── ShinhanBankDao.java
│            └── Transaction.java
├── simplelogger.properties
lib/
├── ojdbc6.jar
├── slf4j-api-1.7.30.jar
└── slf4j-simple-1.7.30.jar

Connect to the SCOTT account with SQL*PLUS and create the following tables and a trigger that inserts transaction history data in the transaction table whenever your account balance changes.

create table bankaccount (
	accountno varchar2(50),
	owner varchar2(20) not null,
	balance number,
	kind varchar2(10),
	constraint PK_BANKACCOUNT primary key(accountno),
	constraint CK_BANKACCOUNT_NORMAL 
		CHECK (balance >= CASE WHEN kind='NORMAL' THEN 0 END),
	constraint CK_BANKACCOUNT_KIND CHECK (kind in ('NORMAL', 'MINUS'))
);  

create table transaction (
    transactiondate timestamp,
    kind varchar2(10),
    amount number,
    balance number,
    accountno varchar2(50),
    constraint FK_TRANSACTION FOREIGN KEY(accountno)
    	REFERENCES bankaccount(accountno)
);

create or replace trigger bank_trigger
after insert or update of balance on bankaccount
for each row
begin
	if :new.balance > :old.balance then
		insert into transaction 
		values 
		(
			systimestamp,
			'DEPOSIT',
			:new.balance - :old.balance,
			:new.balance,
			:old.accountno
		);
	end if;
	if :new.balance < :old.balance then
		insert into transaction 
		values 
		(
			systimestamp,
			'WITHDRAW',
			:old.balance - :new.balance,
			:new.balance,
			:old.accountno
		);
	end if;
end;
/

Before modularization, check if the program works.

Test on Windows

Compile

C:\ Command Prompt
mkdir out
set classpath=lib\slf4j-api-1.7.30.jar
javac -d out src\net\java_school\bank\*.java

Copy properties to out/ directory.

C:\ Command Prompt
copy src\simplelogger.properties out\

Run

C:\ Command Prompt
set classpath=lib\slf4j-api-1.7.30.jar;lib\slf4j-simple-1.7.30.jar;lib\ojdbc6.jar;out
java net.java_school.bank.BankUi

Test on Linux

Compile

CP=jars/slf4j-api-1.7.30.jar
javac -cp $CP -d out -sourcepath src $(find src -name "*.java")

Copy properties to out/ directory.

cp src/simplelogger.properties out/

Run

CP+=:lib/slf4j-simple-1.7.30.jar
CP+=:jars/ojdbc6.jar
java -cp $CP:out net.java_school.bank.BankUi

Before modularization, run the following to check the external library that JavaBank depends on.

jdeps -summary -cp lib/*.jar out
..
out -> lib/slf4j-api-1.7.30.jar
..

We found that JavaBank depends on slf4j-api-1.7.30.jar.
Remember that a library like this should be in the module path.
Jars that are not modules in the module path become automatic modules.

Modularization

Download the custom connection pool modules.

git clone https://github.com/kimjonghoon/java-module-test

Place the JavaBank sources in the src/ folder of java-module-test as follows. --Remove the main.app module and net.java_school.db.dbpool.mysql in java-module-test--

src/
├── net.java_school.javabank
│   ├── net
│   │   └── java_school
│   │       └── bank
│   │            ├── Account.java
│   │            ├── Bank.java
│   │            ├── BankDao.java
│   │            ├── BankUi.java
│   │            ├── ShinhanBank.java
│   │            ├── ShinhanBankDao.java
│   │            └── Transaction.java
│   ├── module-info.java
│   └── simplelogger.properties
├── net.java_school.db.dbpool.api
│   ├── net
│   │   └── java_school
│   │       └── db
│   │           └── dbpool
│   │               └── api
│   │                   └── ConnectionManageable.java
│   └── module-info.java
├── net.java_school.db.dbpool
│   ├── net
│   │   └── java_school
│   │       └── db
│   │           └── dbpool
│   │               ├── DBConnectionPool.java
│   │               ├── DBConnectionPoolManager.java
│   │               └── ConnectionManager.java
│   └── module-info.java
├── net.java_school.db.dbpool.oracle
│   ├── net
│   │   └── java_school
│   │       └── db
│   │           └── dbpool
│   │               └── oracle
│   │                   └── OracleConnectionManager.java
│   ├── module-info.java
│   └── oracle.properties
lib/
├── slf4j-simple-1.7.30.jar
jars/
├── ojdbc6.jar
└── slf4j-api-1.7.30.jar

Add jars/ to the module path.
Non-module jars in the module path become automatic modules.
Add lib/ to the classpath.
Non-module jars in the classpath become unnamed modules.
Only automatic modules can read unnamed modules.
You can easily infer that slf4j-api will read slf4j-simple from their name.

Create the module-info.java of the net.java_school.javabank module as follows.

module net.java_school.javabank {
  requires net.java_school.db.dbpool.api;
  
  uses net.java_school.db.dbpool.api.ConnectionManageable;
}

Compile to see how the names of the automatic modules are determined.

Compile on Windows

C:\ Command Prompt
javac -p jars -d out --module-source-path src ^
-m net.java_school.javabank,net.java_school.db.dbpool.api,
net.java_school.db.dbpool,net.java_school.db.dbpool.oracle

Compile on Linux

javac -p jars -d out --module-source-path src $(find src -name "*.java")

Compile error occurs.

src/net.java_school.javabank/net/java_school/bank/ShinhanBankDao.java:12: error: package org.slf4j is not visible
import org.slf4j.Logger;
          ^
  (package org.slf4j is declared in module org.slf4j, but module net.java_school.javabank does not read it)
src/net.java_school.javabank/net/java_school/bank/ShinhanBankDao.java:13: error: package org.slf4j is not visible
import org.slf4j.LoggerFactory;
          ^
  (package org.slf4j is declared in module org.slf4j, but module net.java_school.javabank does not read it)
2 errors

(package org.slf4j is declared in module org.slf4j, but module net.java_school.javabank does not read it)
The module name can be found in the above error message.
Edit the module-info.java in net.java_school.javabank module as follows.

module net.java_school.javabank {
  requires net.java_school.db.dbpool.api;
  requires org.slf4j;
  
  uses net.java_school.db.dbpool.api.ConnectionManageable;
}

Test on Windows

Compile

C:\ Command Prompt
javac -p jars -d out --module-source-path src ^
-m net.java_school.javabank,net.java_school.db.dbpool.api,
net.java_school.db.dbpool,net.java_school.db.dbpool.oracle

Copy properties to the module directory.

C:\ Command Prompt
copy src\net.java_school.db.dbpool.oracle\oracle.properties ^
out\net.java_school.db.dbpool.oracle\
copy src\net.java_school.javabank\simplelogger.properties ^
out\net.java_school.javabank\

Run

C:\ Command Prompt
set classpath=lib\slf4j-simple-1.7.30.jar
java -p jars;out ^ 
-m net.java_school.javabank/net.java_school.bank.BankUi

Test on Linux

Compile

javac -p jars -d out --module-source-path src $(find src -name "*.java")

Copy properties to the module directory.

cp src/net.java_school.db.dbpool.oracle/oracle.properties \
out/net.java_school.db.dbpool.oracle/
cp src/net.java_school.javabank/simplelogger.properties \
out/net.java_school.javabank/

Run

CP=lib/slf4j-simple-1.7.30.jar
java -cp $CP -p jars:out \
-m net.java_school.javabank/net.java_school.bank.BankUi
Related Articles References