Java 9 Modules
This article covers a Java 9 module example. You read the following articles before starting.
Custom Connection Pool
The below is not a Java module example.
src/
├── net
│ └── java_school
│ └── db
│ └── dbpool
│ ├── DBConnectionPool.java
│ ├── DBConnectionPoolManager.java
│ └── ConnectionManager.java
├── net
│ └── java_school
│ └── db
│ └── dbpool
│ └── oracle
│ └── OracleConnectionManager.java
├── net
│ └── java_school
│ └── db
│ └── dbpool
│ └── mysql
│ └── MySqlConnectionManager.java
├── net
│ └── java_school
│ └── test
│ └── GetEmp.java
├── mysql.properties
├── oracle.properties
jars/
├── ojdbc6.jar
└── mysql-connector-java-8.0.28.jar
DBConnectionPool.java
package net.java_school.db.dbpool; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Date; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; public class DBConnectionPool { private static final Logger logger = Logger.getLogger(DBConnectionPool.class.getName()); // Number of connections currently in use private int checkedOut; // Free Connection List private Vector<Connection> freeConnections = new Vector<Connection>(); // Maximum number of connections private int maxConn; // Waiting time (maximum time to wait when there is no connection in the pool) private int maxWait; // Connection Pool Name private String poolName; // DB Password private String passwd; // DB URL private String URL; // DB UserID private String userID; // Constructor public DBConnectionPool(String poolName, String URL, String userID, String passwd, int maxConn, int initConn, int waitTime) { this.poolName = poolName; this.URL = URL; this.userID = userID; this.passwd = passwd; this.maxConn = maxConn; this.maxWait = waitTime; for (int i = 0; i < initConn; i++) { freeConnections.addElement(newConnection()); } } // Returning Connection // @param con : Connection to return public synchronized void freeConnection(Connection con) { freeConnections.addElement(con); checkedOut--; //Notify thread waiting to get Connection notifyAll(); } // Get Connection @SuppressWarnings("resource") public synchronized Connection getConnection() { Connection con = null; //If Connection is in Free List, get the first of List if (freeConnections.size() > 0) { con = (Connection) freeConnections.firstElement(); freeConnections.removeElementAt(0); try { //If the connection is closed by the DBMS, request again if (con.isClosed()) { logger.log(Level.SEVERE, "Removed bad connection from " + poolName); con = getConnection(); } } //If strange connection occurs, request again catch (SQLException e) { logger.log(Level.SEVERE, "Removed bad connection from " + poolName); con = getConnection(); } } //If Connection is not in Free List, create new else if (maxConn == 0 || checkedOut < maxConn) { con = newConnection(); } if (con != null) { checkedOut++; } return con; } // Get Connection // @param timeout : Maximum Wait Time to Obtain a Connection public synchronized Connection getConnection(long timeout) { long startTime = new Date().getTime(); Connection con; while ((con = getConnection()) == null) { try { wait(timeout * maxWait); } catch (InterruptedException e) {} if ((new Date().getTime() - startTime) >= timeout) { //Wait timeout return null; } } return con; } // Get Connection private Connection newConnection() { Connection con = null; try { if (userID == null) { con = DriverManager.getConnection(URL); } else { con = DriverManager.getConnection(URL, userID, passwd); } logger.info("Created a new connection in pool " + poolName); } catch (SQLException e) { StringBuffer sb = new StringBuffer(); sb.append("Can't create a new connection for "); sb.append(URL); sb.append(" user: "); sb.append(userID); sb.append(" passwd: "); sb.append(passwd); logger.log(Level.SEVERE, sb.toString()); return null; } return con; } }
DBConnectionPoolManager.java
package net.java_school.db.dbpool; import java.sql.Connection; import java.util.Hashtable; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; public class DBConnectionPoolManager { private static final Logger logger = Logger.getLogger(DBConnectionPoolManager.class.getName()); // To apply the singleton pattern to the DBConnectionPoolManager (keep only one instance), // declare it as static static private DBConnectionPoolManager instance; private Vector<String> drivers = new Vector<String>(); private Hashtable<String, DBConnectionPool> pools = new Hashtable<String, DBConnectionPool>(); // Obtaining instance of DBConnectionPoolManager // @return DBConnectionManger static synchronized public DBConnectionPoolManager getInstance() { if (instance == null) { instance = new DBConnectionPoolManager(); } return instance; } // Default Constructor private DBConnectionPoolManager() {} // Send current Connection to Free Connection List // @param name : Pool Name // @param con : Connection public void freeConnection(String name, Connection con) { DBConnectionPool pool = (DBConnectionPool) pools.get(name); if (pool != null) { pool.freeConnection(con); } logger.info("One Connection of " + name + " was freed"); } // Obtain Open Connection. // Creates a new connection if there are no open connections and the maximum number // of connections has not been reached. // Waits for the default wait time when there are no open connections currently // and the maximum number of connections is in use. // @param name : Pool Name // @return Connection : The connection or null public Connection getConnection(String name) { DBConnectionPool pool = (DBConnectionPool) pools.get(name); if (pool != null) { return pool.getConnection(10); } return null; } // Create a Connection Pool // @param poolName : Name of Pool to create // @param url : DB URL // @param userID : DB UserID // @param passwd : DB Password private void createPools(String poolName, String url, String userID, String passwd, int maxConn, int initConn, int maxWait) { DBConnectionPool pool = new DBConnectionPool(poolName, url, userID, passwd, maxConn, initConn, maxWait); pools.put(poolName, pool); logger.info("Initialized pool " + poolName); } // Initialization public void init(String poolName, String driver, String url, String userID, String passwd, int maxConn, int initConn, int maxWait) { loadDrivers(driver); createPools(poolName, url, userID, passwd, maxConn, initConn, maxWait); } // JDBC Driver Loading // @param driverClassName : The JDBC driver for the DB you want to use. private void loadDrivers(String driverClassName) { try { Class.forName(driverClassName); drivers.addElement(driverClassName); logger.info("Registered JDBC driver " + driverClassName); } catch (Exception e) { logger.log(Level.SEVERE, "Can't register JDBC driver: " + driverClassName); } } public Hashtable<String,DBConnectionPool> getPools() { return pools; } public int getDriverNumber() { return drivers.size(); } }
ConnectionManager.java
package net.java_school.db.dbpool; import java.sql.Connection; public abstract class ConnectionManager { protected DBConnectionPoolManager poolManager; protected String poolName; public ConnectionManager() { this.poolManager = DBConnectionPoolManager.getInstance(); } public Connection getConnection() { return (poolManager.getConnection(poolName)); } public void freeConnection(Connection con) { poolManager.freeConnection(poolName, con); } public abstract void initPoolManager(String poolName, String driver, String url, String userID, String passwd, int maxConn, int initConn, int maxWait); public int getDriverNumber() { return poolManager.getDriverNumber(); } }
OracleConnectionManager.java
package net.java_school.db.dbpool.oracle; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URL; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import net.java_school.db.dbpool.ConnectionManager; public class OracleConnectionManager extends ConnectionManager { private static final Logger logger = Logger.getLogger(OracleConnectionManager.class.getName()); public OracleConnectionManager() { this.poolName = "oracle"; String configFile = "oracle.properties"; ClassLoader resource = this.getClass().getClassLoader(); URL path = resource.getResource(configFile); try { Properties prop = new Properties(); FileInputStream inputStream = new FileInputStream(new File(path.getFile())); prop.load(inputStream); String dbServer = prop.getProperty("dbServer"); String port = prop.getProperty("port"); String dbName = prop.getProperty("dbName"); String userID = prop.getProperty("userID"); String passwd = prop.getProperty("passwd"); int maxConn = Integer.parseInt(prop.getProperty("maxConn")); int initConn = Integer.parseInt(prop.getProperty("initConn")); int maxWait = Integer.parseInt(prop.getProperty("maxWait")); String driver = "oracle.jdbc.driver.OracleDriver"; String JDBCDriverType = "jdbc:oracle:thin"; String url = JDBCDriverType + ":@" + dbServer + ":" + port + ":" + dbName; initPoolManager(this.poolName, driver, url, userID, passwd, maxConn, initConn, maxWait); } catch (IOException e) { logger.log(Level.SEVERE, "Error reading properties of " + configFile); throw new RuntimeException(e); } } @Override public void initPoolManager(String poolName, String driver, String url, String userID, String passwd, int maxConn, int initConn, int maxWait) { this.poolManager.init(poolName, driver, url, userID, passwd, maxConn, initConn, maxWait); } }
MySqlConnectionManager.java
package net.java_school.db.dbpool.mysql; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URL; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import net.java_school.db.dbpool.ConnectionManager; public class MySqlConnectionManager extends ConnectionManager { private static final Logger logger = Logger.getLogger(MySqlConnectionManager.class.getName()); public MySqlConnectionManager() { this.poolName = "mysql"; String configFile = "mysql.properties"; ClassLoader resource = this.getClass().getClassLoader(); URL path = resource.getResource(configFile); try { Properties prop = new Properties(); FileInputStream inputStream = new FileInputStream(new File(path.getFile())); prop.load(inputStream); String dbServer = prop.getProperty("dbServer"); String port = prop.getProperty("port"); String dbName = prop.getProperty("dbName"); String userID = prop.getProperty("userID"); String passwd = prop.getProperty("passwd"); int maxConn = Integer.parseInt(prop.getProperty("maxConn")); int initConn = Integer.parseInt(prop.getProperty("initConn")); int maxWait = Integer.parseInt(prop.getProperty("maxWait")); String driver = "com.mysql.jdbc.Driver"; String JDBCDriverType = "jdbc:mysql"; String url = JDBCDriverType + "://" + dbServer + ":" + port + "/" + dbName + "?useUnicode=true&characterEncoding=UTF-8&useSSL=false"; initPoolManager(this.poolName, driver, url, userID, passwd, maxConn, initConn, maxWait); } catch (IOException e) { logger.log(Level.SEVERE, "Error reading properties of " + configFile); throw new RuntimeException(e); } } @Override public void initPoolManager(String poolName, String driver, String url, String userID, String passwd, int maxConn, int initConn, int maxWait) { this.poolManager.init(poolName, driver, url, userID, passwd, maxConn, initConn, maxWait); } }
oracle.properties
############################################ # Database Connection Properties for Oracle ############################################ # Database Server Name OR IP address dbServer = 127.0.0.1 # The port number your DB server listents to. port = 1521 # Database Name dbName = XE # Database User userID = scott # Database Password passwd = tiger # Maximum Connection Number maxConn = 20 # Inital Connection Number initConn = 5 # Maximum Wait Time maxWait = 5
mysql.properties
############################################ # Database Connection Properties for MySQL ############################################ # Database Server Name OR IP address dbServer = localhost # The port number your DB server listents to. port = 3306 # Database Name dbName = xe # Database User userID = scott # Database Password passwd = tiger # Maximum Connection Number maxConn = 20 # Inital Connection Number initConn = 5 # Maximum Wait Time maxWait = 5
Create a MySQL user and database as follows:
mysql --user=root --password mysql create user 'scott'@'%' identified by 'tiger'; grant all privileges on *.* to 'scott'@'%'; create database xe; exit; mysql --user=scott --password xe CREATE TABLE DEPT ( DEPTNO DECIMAL(2), DNAME VARCHAR(14), LOC VARCHAR(13), CONSTRAINT PK_DEPT PRIMARY KEY (DEPTNO) ); CREATE TABLE EMP ( EMPNO DECIMAL(4), ENAME VARCHAR(10), JOB VARCHAR(9), MGR DECIMAL(4), HIREDATE DATE, SAL DECIMAL(7,2), COMM DECIMAL(7,2), DEPTNO DECIMAL(2), CONSTRAINT PK_EMP PRIMARY KEY (EMPNO), CONSTRAINT FK_DEPTNO FOREIGN KEY (DEPTNO) REFERENCES DEPT(DEPTNO) ); CREATE TABLE SALGRADE ( GRADE TINYINT, LOSAL SMALLINT, HISAL SMALLINT ); INSERT INTO DEPT VALUES (10,'ACCOUNTING','NEW YORK'); INSERT INTO DEPT VALUES (20,'RESEARCH','DALLAS'); INSERT INTO DEPT VALUES (30,'SALES','CHICAGO'); INSERT INTO DEPT VALUES (40,'OPERATIONS','BOSTON'); INSERT INTO EMP VALUES (7369,'SMITH','CLERK',7902,STR_TO_DATE('17-12-1980','%d-%m-%Y'),800,NULL,20); INSERT INTO EMP VALUES (7499,'ALLEN','SALESMAN',7698,STR_TO_DATE('20-2-1981','%d-%m-%Y'),1600,300,30); INSERT INTO EMP VALUES (7521,'WARD','SALESMAN',7698,STR_TO_DATE('22-2-1981','%d-%m-%Y'),1250,500,30); INSERT INTO EMP VALUES (7566,'JONES','MANAGER',7839,STR_TO_DATE('2-4-1981','%d-%m-%Y'),2975,NULL,20); INSERT INTO EMP VALUES (7654,'MARTIN','SALESMAN',7698,STR_TO_DATE('28-9-1981','%d-%m-%Y'),1250,1400,30); INSERT INTO EMP VALUES (7698,'BLAKE','MANAGER',7839,STR_TO_DATE('1-5-1981','%d-%m-%Y'),2850,NULL,30); INSERT INTO EMP VALUES (7782,'CLARK','MANAGER',7839,STR_TO_DATE('9-6-1981','%d-%m-%Y'),2450,NULL,10); INSERT INTO EMP VALUES (7788,'SCOTT','ANALYST',7566,STR_TO_DATE('13-7-1987','%d-%m-%Y')-85,3000,NULL,20); INSERT INTO EMP VALUES (7839,'KING','PRESIDENT',NULL,STR_TO_DATE('17-11-1981','%d-%m-%Y'),5000,NULL,10); INSERT INTO EMP VALUES (7844,'TURNER','SALESMAN',7698,STR_TO_DATE('8-9-1981','%d-%m-%Y'),1500,0,30); INSERT INTO EMP VALUES (7876,'ADAMS','CLERK',7788,STR_TO_DATE('13-7-1987', '%d-%m-%Y'),1100,NULL,20); INSERT INTO EMP VALUES (7900,'JAMES','CLERK',7698,STR_TO_DATE('3-12-1981','%d-%m-%Y'),950,NULL,30); INSERT INTO EMP VALUES (7902,'FORD','ANALYST',7566,STR_TO_DATE('3-12-1981','%d-%m-%Y'),3000,NULL,20); INSERT INTO EMP VALUES (7934,'MILLER','CLERK',7782,STR_TO_DATE('23-1-1982','%d-%m-%Y'),1300,NULL,10); INSERT INTO SALGRADE VALUES (1,700,1200); INSERT INTO SALGRADE VALUES (2,1201,1400); INSERT INTO SALGRADE VALUES (3,1401,2000); INSERT INTO SALGRADE VALUES (4,2001,3000); INSERT INTO SALGRADE VALUES (5,3001,9999); COMMIT; exit;
GetEmp.java
package net.java_school.test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import net.java_school.db.dbpool.mysql.MySqlConnectionManager; import net.java_school.db.dbpool.oracle.OracleConnectionManager; public class GetEmp { public static void main(String[] args) { OracleConnectionManager oracleConnectionManager = new OracleConnectionManager(); Connection con = null; PreparedStatement stmt = null; ResultSet rs = null; String sql = "SELECT * FROM EMP"; try { con = oracleConnectionManager.getConnection(); stmt = con.prepareStatement(sql); rs = stmt.executeQuery(); while (rs.next()) { String empno = rs.getString(1); String ename = rs.getString(2); String job = rs.getString(3); String mgr = rs.getString(4); String hiredate = rs.getString(5); String sal = rs.getString(6); String comm = rs.getString(7); String depno = rs.getString(8); System.out.println(empno + " : " + ename + " : " + job + " : " + mgr + " : " + hiredate + " : " + sal + " : " + comm + " : " + depno); } } catch (Exception e) { e.printStackTrace(); } finally { try { rs.close(); } catch (SQLException e) {} try { stmt.close(); } catch (SQLException e) {} oracleConnectionManager.freeConnection(con); } MySqlConnectionManager mysqlConnectionManager = new MySqlConnectionManager(); try { con = mysqlConnectionManager.getConnection(); stmt = con.prepareStatement(sql); rs = stmt.executeQuery(); while (rs.next()) { String empno = rs.getString(1); String ename = rs.getString(2); String job = rs.getString(3); String mgr = rs.getString(4); String hiredate = rs.getString(5); String sal = rs.getString(6); String comm = rs.getString(7); String depno = rs.getString(8); System.out.println(empno + " : " + ename + " : " + job + " : " + mgr + " : " + hiredate + " : " + sal + " : " + comm + " : " + depno); } } catch (Exception e) { e.printStackTrace(); } finally { try { rs.close(); } catch (SQLException e) {} try { stmt.close(); } catch (SQLException e) {} mysqlConnectionManager.freeConnection(con); } System.out.println("Driver Number: " + mysqlConnectionManager.getDriverNumber()); } }
Oracle JDBC Driver Download
MySQL JDBC Driver Download
Copy the JDBC drivers to the jars/ folder.
Test on Windows
Run the dir command and verify that the output looks like this:
C:\ Command PromptVolume in drive H has no label. Volume Serial Number is 26D8-3E15 Directory of H:\this-is-not-modules-example 03/05/2020 05:41 AM <DIR> . 03/05/2020 05:41 AM <DIR> .. 03/05/2020 05:35 AM <DIR> jars 03/05/2020 05:35 AM <DIR> src
Create a directory where Javac will generate byte codes.
C:\ Command Promptmkdir out
Compile in the following order:
C:\ Command Promptjavac -d out src\net\java_school\db\dbpool\*.java javac -d out src\net\java_school\db\dbpool\oracle\OracleConnectionManager.java javac -d out src\net\java_school\db\dbpool\mysql\MySqlConnectionManager.java javac -d out src\net\java_school\test\GetEmp.java
Copy the Java property files into the out directory.
C:\ Command Promptxcopy src\*.properties out
Run
C:\ Command Promptset classpath=jars/ojdbc6.jar;jars/mysql-connector-java-8.0.28.jar;out java net.java_school.test.GetEmp
Test on Linux
Run the ls command and verify that the output looks like this:
jars src
Compile
javac -d out -sourcepath src $(find src -name "*.java")
Copy the Java property files into the out directory.
cp $(find src -name '*.properties') out
Run
CP=jars/ojdbc6.jar CP+=:jars/mysql-connector-java-8.0.28.jar java -cp $CP:out net.java_school.test.GetEmp
Sep 17, 2019 11:46:41 AM net.java_school.db.dbpool.DBConnectionPoolManager loadDrivers INFO: Registered JDBC driver oracle.jdbc.driver.OracleDriver Sep 17, 2019 11:46:42 AM net.java_school.db.dbpool.DBConnectionPool newConnection INFO: Created a new connection in pool oracle Sep 17, 2019 11:46:42 AM net.java_school.db.dbpool.DBConnectionPool newConnection INFO: Created a new connection in pool oracle Sep 17, 2019 11:46:42 AM net.java_school.db.dbpool.DBConnectionPool newConnection INFO: Created a new connection in pool oracle Sep 17, 2019 11:46:42 AM net.java_school.db.dbpool.DBConnectionPool newConnection INFO: Created a new connection in pool oracle Sep 17, 2019 11:46:42 AM net.java_school.db.dbpool.DBConnectionPool newConnection INFO: Created a new connection in pool oracle Sep 17, 2019 11:46:42 AM net.java_school.db.dbpool.DBConnectionPoolManager createPools INFO: Initialized pool oracle 7369 : SMITH : CLERK : 7902 : 1980-12-17 00:00:00 : 800 : null : 20 7499 : ALLEN : SALESMAN : 7698 : 1981-02-20 00:00:00 : 1600 : 300 : 30 7521 : WARD : SALESMAN : 7698 : 1981-02-22 00:00:00 : 1250 : 500 : 30 7566 : JONES : MANAGER : 7839 : 1981-04-02 00:00:00 : 2975 : null : 20 7654 : MARTIN : SALESMAN : 7698 : 1981-09-28 00:00:00 : 1250 : 1400 : 30 7698 : BLAKE : MANAGER : 7839 : 1981-05-01 00:00:00 : 2850 : null : 30 7782 : CLARK : MANAGER : 7839 : 1981-06-09 00:00:00 : 2450 : null : 10 7788 : SCOTT : ANALYST : 7566 : 1987-07-13 00:00:00 : 3000 : null : 20 7839 : KING : PRESIDENT : null : 1981-11-17 00:00:00 : 5000 : null : 10 7844 : TURNER : SALESMAN : 7698 : 1981-09-08 00:00:00 : 1500 : 0 : 30 7876 : ADAMS : CLERK : 7788 : 1987-07-13 00:00:00 : 1100 : null : 20 7900 : JAMES : CLERK : 7698 : 1981-12-03 00:00:00 : 950 : null : 30 7902 : FORD : ANALYST : 7566 : 1981-12-03 00:00:00 : 3000 : null : 20 7934 : MILLER : CLERK : 7782 : 1982-01-23 00:00:00 : 1300 : null : 10 Sep 17, 2019 11:46:42 AM net.java_school.db.dbpool.DBConnectionPoolManager freeConnection INFO: One Connection of oracle was freed Sep 17, 2019 11:46:42 AM net.java_school.db.dbpool.DBConnectionPoolManager loadDrivers INFO: Registered JDBC driver com.mysql.jdbc.Driver Sep 17, 2019 11:46:43 AM net.java_school.db.dbpool.DBConnectionPool newConnection INFO: Created a new connection in pool mysql Sep 17, 2019 11:46:43 AM net.java_school.db.dbpool.DBConnectionPool newConnection INFO: Created a new connection in pool mysql Sep 17, 2019 11:46:43 AM net.java_school.db.dbpool.DBConnectionPool newConnection INFO: Created a new connection in pool mysql Sep 17, 2019 11:46:43 AM net.java_school.db.dbpool.DBConnectionPool newConnection INFO: Created a new connection in pool mysql Sep 17, 2019 11:46:43 AM net.java_school.db.dbpool.DBConnectionPool newConnection INFO: Created a new connection in pool mysql Sep 17, 2019 11:46:43 AM net.java_school.db.dbpool.DBConnectionPoolManager createPools INFO: Initialized pool mysql 7369 : SMITH : CLERK : 7902 : 1980-12-17 : 800.00 : null : 20 7499 : ALLEN : SALESMAN : 7698 : 1981-02-20 : 1600.00 : 300.00 : 30 7521 : WARD : SALESMAN : 7698 : 1981-02-22 : 1250.00 : 500.00 : 30 7566 : JONES : MANAGER : 7839 : 1981-04-02 : 2975.00 : null : 20 7654 : MARTIN : SALESMAN : 7698 : 1981-09-28 : 1250.00 : 1400.00 : 30 7698 : BLAKE : MANAGER : 7839 : 1981-05-01 : 2850.00 : null : 30 7782 : CLARK : MANAGER : 7839 : 1981-06-09 : 2450.00 : null : 10 7788 : SCOTT : ANALYST : 7566 : 1987-06-28 : 3000.00 : null : 20 7839 : KING : PRESIDENT : null : 1981-11-17 : 5000.00 : null : 10 7844 : TURNER : SALESMAN : 7698 : 1981-09-08 : 1500.00 : 0.00 : 30 7876 : ADAMS : CLERK : 7788 : 1987-07-13 : 1100.00 : null : 20 7900 : JAMES : CLERK : 7698 : 1981-12-03 : 950.00 : null : 30 7902 : FORD : ANALYST : 7566 : 1981-12-03 : 3000.00 : null : 20 7934 : MILLER : CLERK : 7782 : 1982-01-23 : 1300.00 : null : 10 Sep 17, 2019 11:46:43 AM net.java_school.db.dbpool.DBConnectionPoolManager freeConnection INFO: One Connection of mysql was freed Driver Number: 2
Modularization
Let's divide the example into parts. Each one will be a module.
1st module : net.java_school.db.dbpool (Module Name) DBConnectionPool.java DBConnectionPoolManager.java ConnectionManager.java
2nd module : net.java_school.db.dbpool.oracle OracleConnectionManager.java oracle.properties
3rd module : net.java_school.db.dbpool.mysql MySqlConnectionManager.java mysql.properties
4th module : main.app GetEmp.java
5th module ojdbc6.jar
6th module mysql-connector-java-8.0.28.jar
Change the directory structure as shown below.
Just under src/, there should be folders with module names.
src/
├── net.java_school.db.dbpool (Module Name)
│ ├── 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
├── net.java_school.db.dbpool.mysql
│ ├── net
│ │ └── java_school
│ │ └── db
│ │ └── dbpool
│ │ └── mysql
│ │ └── MySqlConnectionManager.java
│ ├── module-info.java
│ └── mysql.properties
├── main.app
│ ├── net
│ │ └── java_school
│ │ └── test
│ │ └── GetEmp.java
│ └── module-info.java
jars/
├── ojdbc6.jar
└── mysql-connector-java-8.0.28.jar
1st Module : net.java_school.db.dbpool
module-info.java
module net.java_school.db.dbpool { requires java.logging; requires transitive java.sql; exports net.java_school.db.dbpool; }
requires java.logging;
The requires keyword specifies on which this module depends the external module.
java.logging is a module name, not a package name.
requires transitive java.sql;
The module that depends on net.java_school.db.dbpool will automatically depend on java.sql.
exports net.java_school.db.dbpool;
The exports keyword specifies the package that this module exports.
net.java_school.db.dbpool is the package name and the the module name.
- Although module A requires module B, module A can only use the public elements of packages exported by module B.
2nd Module : net.java_school.db.dbpool.oracle
module-info.java
module net.java_school.db.dbpool.oracle { requires net.java_school.db.dbpool; exports net.java_school.db.dbpool.oracle; }
3rd Module : net.java_school.db.dbpool.mysql
module-info.java
module net.java_school.db.dbpool.mysql { requires net.java_school.db.dbpool; exports net.java_school.db.dbpool.mysql; }
4th Module : main.app
module-info.java
module main.app { requires java.sql; requires net.java_school.db.dbpool.oracle; requires net.java_school.db.dbpool.mysql; }
See below to modify the code that reads the Java property file.
OracleConnectionManager.java
public OracleConnectionManager() { this.poolName = "oracle"; String configFile = "oracle.properties"; Class<?> clazz = OracleConnectionManager.class; Module m = clazz.getModule(); try { InputStream inputStream = m.getResourceAsStream(configFile); Properties prop = new Properties(); prop.load(inputStream); //..Omit..
MySqlConnectionManager.java
public MySqlConnectionManager() { this.poolName = "mysql"; String configFile = "mysql.properties"; Class<?> clazz = MySqlConnectionManager.class; Module m = clazz.getModule(); try { InputStream inputStream = m.getResourceAsStream(configFile); Properties prop = new Properties(); prop.load(inputStream); //..Omit..
Test on Windows
Compile
C:\ Command Promptjavac -d out --module-source-path src ^ -m main.app,net.java_school.db.dbpool, ^ net.java_school.db.dbpool.oracle,net.java_school.db.dbpool.mysql
--module-source-path: Specify where to find input source files for multiple modules.
-m: Compile only the specified module.
Copy the Java property file into each module directory.
C:\ Command Promptcopy src\net.java_school.db.dbpool.oracle\oracle.properties ^ out\net.java_school.db.dbpool.oracle\ copy src\net.java_school.db.dbpool.mysql\mysql.properties ^ out\net.java_school.db.dbpool.mysql\
Run
C:\ Command Promptjava -p jars:out -m main.app/net.java_school.test.GetEmp
-p: module path
-m: module/base class --For modular jars, enter module name only--
Non-modular jars in the module path become automatic modules. Java VM interprets automatic modules as requires transitive all other modules on the module path. It means that automatic modules can read all packages exported by all named modules in the module path.
Automatic Modules export all their packages.
Test on Linux
Compile
javac -d out --module-source-path src $(find src -name "*.java")
Copy the Java property files into each module directory.
cp src/net.java_school.db.dbpool.oracle/oracle.properties \ out/net.java_school.db.dbpool.oracle/ cp src/net.java_school.db.dbpool.mysql/mysql.properties \ out/net.java_school.db.dbpool.mysql/
Run
java -p jars:out -m main.app/net.java_school.test.GetEmp
Using ServiceLoader
Using ServiceLoader, you can make services and service implementations into separate modules.
module main.app { requires net.java_school.db.dbpool; uses net.java_school.db.dbpool.ConnectionManager; }
uses net.java_school.db.dbpool.ConnectionManager;
A uses keyword specifies a service used by this module.
The net.java_school.db.dbpool.ConnectionManager is an abstract class.
Abstract or implementation classes can also be services.
module net.java_school.db.dbpool.oracle { requires net.java_school.db.dbpool; provides net.java_school.db.dbpool.ConnectionManager with net.java_school.db.dbpool.oracle.OracleConnectionManager; }
provides net.java_school.db.dbpool.ConnectionManagerService
with net.java_school.db.dbpool.oracle.OracleConnectionManagerImplementation;
module net.java_school.db.dbpool.mysql { requires net.java_school.db.dbpool; provides net.java_school.db.dbpool.ConnectionManager with net.java_school.db.dbpool.mysql.MySqlConnectionManager; }
provides net.java_school.db.dbpool.ConnectionManagerService
with net.java_school.db.dbpool.mysql.MySqlConnectionManagerImplementation;
Note that the module providing the implementationProvider does not export any packages of its own. Therefore, the GetEmp class of the main.app moduleConsumer cannot use any type in Providers.
package net.java_school.test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ServiceLoader; import net.java_school.db.dbpool.ConnectionManager; public class GetEmp { public static void main(String[] args) { Iterable<ConnectionManager> managers = ServiceLoader.load(ConnectionManager.class); for (ConnectionManager manager : managers) { Connection con = null; PreparedStatement stmt = null; ResultSet rs = null; String sql = "SELECT * FROM EMP"; try { con = manager.getConnection(); stmt = con.prepareStatement(sql); rs = stmt.executeQuery(); while (rs.next()) { String empno = rs.getString(1); String ename = rs.getString(2); String job = rs.getString(3); String mgr = rs.getString(4); String hiredate = rs.getString(5); String sal = rs.getString(6); String comm = rs.getString(7); String depno = rs.getString(8); System.out.println(empno + " : " + ename + " : " + job + " : " + mgr + " : " + hiredate + " : " + sal + " : " + comm + " : " + depno); } } catch (Exception e) { e.printStackTrace(); } finally { try { rs.close(); } catch (SQLException e) {} try { stmt.close(); } catch (SQLException e) {} manager.freeConnection(con); } System.out.println("Driver Number: " + manager.getDriverNumber()); } } }
The service and implementation are now entirely separated into their respective modules.
If only net.java_school.db.dbpool.oracle or net.java_school.db.dbpool.mysql is in the module path, you can run main.app. Even if neither net.java_school.db.dbpool.oracle nor net.java_school.db.dbpool.mysql is in the module path, compilation succeeds.
Filtering Services
Create the following file in net.java_school.db.dbpool module.
package net.java_school.db.dbpool; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface Oracle { public boolean value() default true; }
Add an Oracle annotation to the OracleConnectionManager class in net.java_school.db.dbpool.oracle module.
import net.java_school.db.dbpool.Oracle; @Oracle public class OracleConnectionManager extends ConnectionManager { //.. }
Modify GetEmp class.
import net.java_school.db.dbpool.Oracle; public class GetEmp { public static void main(String[] args) { ServiceLoader<ConnectionManager> managers = ServiceLoader.load(ConnectionManager.class); ConnectionManager manager = managers.stream() .filter(provider -> isOracle(provider.type())) .map(ServiceLoader.Provider::get).findAny().get(); //..Omit.. } private static boolean isOracle(Class<?> clazz) { return clazz.isAnnotationPresent(Oracle.class) && clazz.getAnnotation(Oracle.class).value() == true; } }
Adding Interface
Create the net.java_school.db.dbpool.api module.
Create the following interface in net.java_school.db.dbpool.api module.
ConnectionManageable.java
package net.java_school.db.dbpool.api; import java.sql.Connection; public interface ConnectionManageable { public Connection getConnection(); public void freeConnection(Connection con); public int getDriverNumber(); }
Modify ConnectionManager abstract class in net.java_school.db.dbpool module.
package net.java_school.db.dbpool; import java.sql.Connection; import net.java_school.db.dbpool.api.ConnectionManageable; public abstract class ConnectionManager implements ConnectionManageable { protected DBConnectionPoolManager poolManager; protected String poolName; public ConnectionManager() { this.poolManager = DBConnectionPoolManager.getInstance(); } @Override public Connection getConnection() { return (poolManager.getConnection(poolName)); } @Override public void freeConnection(Connection con) { poolManager.freeConnection(poolName, con); } public abstract void initPoolManager(String poolName, String driver, String url, String userID, String passwd, int maxConn, int initConn, int maxWait); @Override public int getDriverNumber() { return poolManager.getDriverNumber(); } }
Create the module descriptor for net.java_school.db.dbpool.api module.
module net.java_school.db.dbpool.api { requires transitive java.sql; exports net.java_school.db.dbpool.api; }
Modify module descriptors.
module net.java_school.db.dbpool { requires transitive net.java_school.db.dbpool.api; exports net.java_school.db.dbpool; }
module net.java_school.db.dbpool.mysql { requires net.java_school.db.dbpool; provides net.java_school.db.dbpool.api.ConnectionManageable with net.java_school.db.dbpool.mysql.MySqlConnectionManager; }
module net.java_school.db.dbpool.oracle { requires net.java_school.db.dbpool; provides net.java_school.db.dbpool.api.ConnectionManageable with net.java_school.db.dbpool.oracle.OracleConnectionManager; }
module main.app { requires net.java_school.db.dbpool.api; uses net.java_school.db.dbpool.api.ConnectionManageable; }
After moving net.java_school.db.dbpool.Oracle.java to net.java_school.db.dbpool.api module, modify it as bellows:
package net.java_school.db.dbpool.api; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface Oracle { public boolean value() default true; }
Modify OracleConnectionManager in net.java_school.db.dbpool.oracle module.
package net.java_school.db.dbpool.oracle; import net.java_school.db.dbpool.ConnectionManager; import net.java_school.db.dbpool.api.Oracle; @Oracle public class OracleConnectionManager extends ConnectionManager {
Modify GetEmp class.
import net.java_school.db.dbpool.api.ConnectionManageable; import net.java_school.db.dbpool.api.Oracle; public class GetEmp { public static void main(String[] args) { ServiceLoader<ConnectionManageable> managers = ServiceLoader.load(ConnectionManageable.class); ConnectionManageable manager = managers.stream() .filter(provider -> isOracle(provider.type())) .map(ServiceLoader.Provider::get).findAny().get();
Source: https://github.com/kimjonghoon/java-module-test
References