Saturday, May 9, 2020

Create Docker Container for SpringBoot Application

Pre-conditions:

  • understand how to use Docker
  • understand how to create SpringBoot applications
  1. Package your SpringBoot application using Maven - "mvn clean package".
  2. Create a DockerFile
    • FROM openjdk:11-slim
    • COPY ./target/KnowledgeBase-1.0.jar /usr/app/KnowledgeBase-1.0.jar
    • WORKDIR /usr/app
    • RUN sh -c "touch KnowledgeBase-1.0.jar"
    • ENTRYPOINT ["java", "-jar", "KnowledgeBase-1.0.jar"]
  3. Modify pom.xml to add repackage target to have a runnable .jar and maven-jar-plugin to specify mainClass in manifest.
  4. Note: if your SpringBoot access a database (e.g. PostgreSQL) in another Docker container, need to change JDBC from localhost to the IP address of the database container (in application.properties).
  5. Build Docker container using "docker build -t <image_name> ."
  6. Verify the newly build image using "docker images"
  7. Run the Docker container using "docker run --name <container_name> -p 8090:8080 <image_name>". Note: the trigger port can be different from the original one (e.g. 8090:8080)
  8. the container can be stop and start using "docker container stop <container_name>" and "docker container start <container_name>" respectively.
  9. check container logs using "docker container logs <container_name>"

Friday, May 8, 2020

Example of a SpringBoot data service

This is a simple database application using SpringBoot and PostgreSQL. The source code is at https://github.com/jonliu6/KnowledgeBase
  1. create a Maven project template using "mvn archetype:generate".
  2. fill in the application information.
    • groupId, artifactId, version, package
  3. open Maven project file - pom.xml and add SpringBoot and PostgreSQL dependencies.
  4. create application.properties in src/main/resources with the application name, port and database connection information.
  5. set up database and table(s) in PostgreSQL - scripts.
  6. create SpringBoot entry application class (with @SpringBootApplication)
  7. create a data model entity class (e.g. Article.java) mapping to the corresponding table (e.g. t_article).
  8. create a repository interface extends from CrudRepository from Spring Data with the common method declarations (e.g. findAll(), save() and etc.).
  9. create a controller class with org.springframework.web.bind.annotation.RestController.
    • autowire the repository in the controller.
    • create the HTTP RESTful methods in the controller.
  10. build and run the SpringBoot application by running "mvn spring-boot:run"
  11. test the controller method (e.g. http://<server>:<port>/<path>).
    • curl -X GET http://localhost:8080/articles/
    • curl -X GET http://localhost:8080/articles/Test
    • curl -X POST -H "Content-Type: application/json" -d "{"""title""":"""2nd Article for testing purpose""","""category""":"""Test""","""description""":"""This is a test record added from SpringBoot RestController again."""}" http://localhost:8080/articles/add
    • curl -X DELETE http://localhost:8080/articles/10008

Wednesday, May 6, 2020

How to create a Docker container for PostgreSQL


  1. install Docker Desktop from https://www.docker.com/get-started based on your OS.
  2. create your Docker account at https://hub.docker.com and log on.
  3. open a command window (in Windows), and type docker --version to check the docker version.
    • c:\users\testuser>docker --version
    • Docker version 19.03.8, build afacb8b
  4. start Docker Desktop and log on your Docket account.
  5. search postgres from  https://hub.docker.com and click postgres Office Image.
  6. in the command window, type "docker pull postgres" to get the latest PostgreSQL image. Note: by default, the PostgreSQL version is the latest. To get a particular version, execute "docker pull postgres:12.2".
  7. in the command window, type the following to create and start a PostgreSQL container (-p for port number <trigger port #:port# in container>):
    • docker run --name containername -h localhost -p 5435:5432 -e POSTGRES_PASSWORD=mypassword -d postgres:12.2
  8. use "docker ps" or "docker container ls" to show the running container status. "docker ps -a" lists all existing containers.
  9. to stop a docket container, type "docker stop containername"
  10. to restart an existing container, type "docker start containername"
  11. get in the docker container by typing "docker exec -it containername bash"
  12. within the container bash shell, you can then log in PostgreSQL as usual
    • psql -U postgres postgres
  13. if you specified host and port numbers when creating the docker postgres container, you can access from the local machine using postgres client
    • psql -h localhost -p 5435 -U postgres -W
  14. to remove the container, use "docker rm -f containername"
  15. to list docker images, type "docker images" or "docker image ls"
  16. to remove an image by name, use "docker image rm -f imagename", or by id using "docker image rmi -f 93d5585e579d"

Thursday, November 1, 2018

Java Application accessing Oracle Database by using Hibernate


1. Download Hibernate from http://www.hibernate.org/downloads or mvnrepository.com

2. Configure Hibernate by hibernate.cfg.xml or hibernate.properties
   hibernate.cfg.xml sample for Oracle
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" 
"http://www.jboss.org/dtd/hibernate/hibernate-configuration-3.0.dtd">
 <!-- // NOTE: if accessing .dtd has a problem, download .dtd to local and refer it from CLASSPATH like below -->
 <!-- <!DOCTYPE hibernate-configuration SYSTEM "classpath://org/hibernate/hibernate-configuration-3.0.dtd"> -->
<hibernate-configuration>
<session-factory> <!-- Oracle -->
<property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property> 
<property name="hibernate.connection.url">jdbc:oracle:thin:@server:port:SID</property> 
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property> 
<property name="hibernate.default_schema">schema_name</property> 
<property name="hibernate.connection.username">user_name</property> 
<property name="hibernate.connection.password">user_password</property>
<property name="hibernate.connection.pool_size">10</property>
<property name="show_sql">true</property> 
<property name="hibernate.current_session_context_class">thread</property> 
<property name="hibernate.jdbc.batch_size">30</property>
</session-factory>
</hibernate-configuration>

   hibernate.properties sample for PostgreSQL
    hibernate.connection.driver_class = org.postgresql.Driver
    hibernate.connection.url = jdbc:postgresql://localhost:5432/mydatabase
    hibernate.connection.username = myuser
    hibernate.connection.password = secret
    hibernate.c3p0.min_size=5
    hibernate.c3p0.max_size=20
    hibernate.c3p0.timeout=1800
    hibernate.c3p0.max_statements=50
    hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect

3. Create a Hibernate Java project in Maven with required dependencies, including:
    database client library files. e.g. ojdbc*.jar for Oracle, postgresql*.jar for PostgreSQL
hibernate-core
hibernate-entitymanager for Hibernate JPA implementation
hibernate-ehcache for using Hibernate cache mechenism
    e.g. in pom.xml
    <properties>
        <hibernate.version>4.3.5.Final</hibernate.version> <!-- bundled with Wildfly 8.1.0 -->
    </properties>
    <dependencies>
      <dependency>
        <groupId>com.oracle</groupId>
        <artifactId>ojdbc</artifactId>
        <version>5.0</version>
      </dependency>
      <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>${hibernate.version}</version>
      </dependency>
      <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>${hibernate.version}</version>
      </dependency>
      <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-ehcache</artifactId>
        <version>${hibernate.version}</version>
      </dependency>
  </dependencies>

4. Create an Entity object mapped with the data structure (e.g. a table or a view) in DB
    For an example of Student Table in DB, the entity class defined as Student in below:
@Entity
 @Table(name="Student_Table")
public class Student {
 @Id
@Column(name="Student_Id")
private String studentId;

@Column(name="First_Name")
private String firstName;

@Column(name="Last_Name")
private String lastName;

public String getStudentId(){ return studentId; }
public void setStudentId(String id) { this.studentId = id; }

public String getFirstName(){ return firstName; }
public void setFirstName(String name) { this.firstName = name; }

public String getLastName(){ return lastName; }
public void setLastName(String name) { this.lastName = name; }
}

5. Map the entity class in hibernate.cfg.xml by adding the following in <session-factory />
    <mapping class="org.freecode.demo.entity.Student" /> <!-- // full class name with package -->

6. Access DB through Hibernate
    public static void main(String[] args) {
String configFile = "hibernate.cfg.xml"; // load Hibernate configuration
Configuration cfg = new Configuration();
cfg.configure(configFile);
StandardServiceRegistryBuilder ssrb = new StandardServiceRegistryBuilder();
ssrb.applySettings(cfg.getProperties());
ServiceRegistry sr = ssrb.build();
SessionFactory sf = cfg.buildSessionFactory(sr); // initialize a session factory
Session s = sf.openSession();
// Session s = sf.getCurrentSession(); // initialize a DB session
// Transaction tx = null;
try {
// tx = s.beginTransaction();
List<ProjectCause> causes = s.createQuery("FROM Student").list(); // create a Hibernate Query to get all student objects from the DB session
// tx.commit();
if (causes != null) {
for (ProjectCause c : causes) {
System.out.println("Student: " + c.getStudentId() + " - " c.getFirstName() + " " + c.getLastName());
}
}
}
catch (HibernateException he) {
he.printStackTrace();
// tx.rollback();
}
finally {
if (s.isOpen()) {
            s.close();
}
}
}

7. Use Hibernate JPA Implementation by creating JPA descriptor - persistence.xml
    <?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
  <persistence-unit name="Hibernate_JPA_Unit" transaction-type="RESOURCE_LOCAL"> <!-- non EJB type -->
<description>Persistence Unit for JPA Implementation</description>
<provider>org.hibernate.ejb.HibernatePersistence</provider>
  <class>org.freecode.demo.entity.Student</class> <!-- need to map the class for WAR, EAR -->
<properties>
  <property name="javax.persistence.jdbc.driver" value="oracle.jdbc.OracleDriver" />
  <property name="javax.persistence.jdbc.url" value="jdbc:oracle:thin:@server:port:SID" />
  <property name="javax.persistence.jdbc.user" value="db user" />
  <property name="javax.persistence.jdbc.password" value="db password" />
  <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
  
  <!-- for caching, require hibernate-ehcache.jar -->
  <property name="hibernate.cache.use_second_level_cache" value="true"/>
  <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"></property>
  <property name="hibernate.cache.use_query_cache" value="true"></property>

  <property name="hibernate.jdbc.batch_size" value="30"/>
  <property name="hibernate.show_sql" value="true" />
  <!-- logger: org.hibernate.SQL, org.hibernate.engine.query, org.hibernate.type, org.hibernate.jdbc -->
  <property name="hibernate.format_sql" value="true"/>
  <property name="hibernate.transaction.flush_before_completion" value="true" />
</properties>
  </persistence-unit>
</persistence>

8. Access DB through Hibernate JPA
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Hibernate_JPA_Unit"); // unit name is defined in persistence.xml
EntityManager em = emf.createEntityManager();

try {
Query qry = em.createQuery("SELECT c FROM Student c");
List<ProjectCause> students = qry.getResultList();
if (causes != null) {
for (Student std : students) {
System.out.println(std.getStudentId() + " - " + std.getFirstName() + " " + c.getLastName());
}
}
}
catch (Exception ex) {
ex.printStackTrace();
}
finally {
if (em.isOpen()) {
em.close();
}
em = null;
if (emf.isOpen()) {
emf.close();
}
emf = null;
}

Monday, October 22, 2018

Configure Different Database Connections in Wildfly/JBoss Application Server


1. download and copy the database driver libraries
   For Oralce, search ojdbc from Oracle, https://www.oracle.com/technetwork/database/features/jdbc/jdbc-ucp-122-3110062.html
   For MS SQL Server, https://www.microsoft.com/en-us/download/details.aspx?id=56615
   For MySQL, https://dev.mysql.com/downloads/connector/j/
   For PostgreSQL, https://jdbc.postgresql.org/download.html

2. install and configure the database drivers in Wildfly/JBoss system modules
   For Oracle, copy ojdbc?.jar (e.g. ojdbc7.jar) to %JBOSS_HOME%/modules/system/layers/base/com/oracle/main (create folders when necessary). Create a configuration file, module.xml in the same folder with the driver file.
       <?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="com.oracle">
  <resources>
<resource-root path="ojdbc5.jar"/>
  </resources>
  <dependencies>
<module name="javax.api"/>
<module name="javax.transaction.api"/>
  </dependencies>
</module>

   For MS SQL Server, copy mssql-jdbc-x.x.x.jre?.jar (e.g. mssql-jdbc-6.2.2.jre7) into %JBOSS_HOME%/modules/system/layers/base/com/microsoft/sqlserver/main. Create a configuration file, module.xml in the same folder with the driver file.
       <?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="com.microsoft.sqlserver">
  <resources>
<resource-root path="mssql-jdbc-6.2.2.jre7.jar"/>
  </resources>
  <dependencies>
<module name="javax.api"/>
<module name="javax.transaction.api"/>
<module name="javax.xml.bind.api"/>
  </dependencies>
</module>

   For MySQL, copy mysql-connector-java-x.x.x.jar (e.g. mysql-connector-java-5.1.44.jar) into %JBOSS_HOME%/modules/system/layers/base/com/mysql/main. Create a configuration file, module.xml in the same folder with the driver file.
   <?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="com.mysql">
  <resources>
<resource-root path="mysql-connector-java-5.1.44.jar"/>
  </resources>
  <dependencies>
<module name="javax.api"/>
<module name="javax.transaction.api"/>
  </dependencies>
</module>

For PostgreSQL, copy postgresql-x.x.x.jre?.jar (e.g. postgresql-9.4.1209.jre6.jar) into %JBOSS_HOME%/modules/system/layers/base/org/postgresql/main. Create a configuration file, module.xml in the same folder with the driver file.
    <?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.postgresql">
  <resources>
<resource-root path="postgresql-9.4.1209.jre6.jar"/>
  </resources>
  <dependencies>
<module name="javax.api"/>
<module name="javax.transaction.api"/>
  </dependencies>
</module>

3. Configure the database drivers and data sources in Wild/JBoss Application Server.
    Open and update the Wildfly/JBoss configuration file (e.g. standalone.xml) by adding the database driver and data source information like the following: (Note: make sure the driver name, module name and etc. match with the configuration previously mentioned)

        <subsystem xmlns="urn:jboss:domain:datasources:2.0">
            <datasources>
                ... ...
<!-- // Connection Pool of Oracle. java:/jdbc/myoracle is used in applications -->
                <datasource jndi-name="java:/jdbc/myoracle" pool-name="myOraclePool" enabled="true" use-java-context="true">
                    <connection-url>jdbc:oracle:thin:@server_name:1521:SID</connection-url>
                    <driver>oracle_driver</driver>
                    <security>
                        <user-name>db_user_name</user-name>
                        <password>db_user_password</password>
                    </security>
                </datasource>

<!-- // Connection Pool for MS SQL Server Express -->
                <datasource jndi-name="java:/jdbc/MsSQLSvrDS" pool-name="MsSQLPool" enabled="true" use-java-context="true">
                    <connection-url>jdbc:sqlserver://server_name\SQLEXPRESS:54126;databaseName=TestDB;integratedSecurity=true;</connection-url>
                    <driver-class>com.microsoft.sqlserver.jdbc.SQLServerDriver</driver-class>
                    <driver>sqlserver_driver</driver>
                    <pool>
                        <min-pool-size>5</min-pool-size>
                        <max-pool-size>10</max-pool-size>
                    </pool>
                    <security>
                        <user-name>db_user_name</user-name>
                        <password>db_user_password</password>
                    </security>
                </datasource>

<!-- // Connection Pool for MySQL -->
                <datasource jndi-name="java:/jdbc/MySQLDS" pool-name="MySQLDS" enabled="true" use-java-context="true">
                    <connection-url>jdbc:mysql://server_name:3306/my_test_db</connection-url>
                    <driver>mysql_driver</driver>
                    <pool>
                        <min-pool-size>1</min-pool-size>
                        <max-pool-size>5</max-pool-size>
                    </pool>
                    <security>
                        <user-name>db_user_name</user-name>
                        <password>db_user_password</password>
                    </security>
                </datasource>

                <!-- // Connection Pool for MySQL -->
                <datasource jta="false" jndi-name="java:/jdbc/PostgreSQLDS" pool-name="PostgreSQLDS" enabled="true" use-ccm="false">
                    <connection-url>jdbc:postgresql://server_name:5432/my_test_db</connection-url>
                    <driver-class>org.postgresql.Driver</driver-class>
                    <driver>postgresql_driver</driver>
                    <pool>
                        <min-pool-size>1</min-pool-size>
                        <max-pool-size>5</max-pool-size>
                    </pool>
                    <security>
                        <user-name>db_user_name</user-name>
                        <password>db_user_password</password>
                    </security>
                </datasource>
                <drivers>
                    ... ...
                    <!-- // Oracle Driver -->
    <driver name="oracle_driver" module="com.oracle">
                        <driver-class>oracle.jdbc.OracleDriver</driver-class>
                    </driver>

                    <!-- // MS SQL Server Driver -->
                    <driver name="sqlserver_driver" module="com.microsoft.sqlserver">
                        <driver-class>com.microsoft.sqlserver.jdbc.SQLServerDriver</driver-class>
                    </driver>
                   
                    <!-- // MySQL Driver -->
                    <driver name="mysql_driver" module="com.mysql">
                        <driver-class>com.mysql.jdbc.Driver</driver-class>
                    </driver>

                    <!-- // PostgreSQL Driver -->
                    <driver name="postgresql_driver" module="org.postgresql">
                        <driver-class>org.postgresql.Driver</driver-class>
                    </driver>
                </drivers>
            </datasources>
        </subsystem>

4. Start the Wildfly/JBoss application server and verify the database connectivities from the server logs.

5. Use the connection pool for JPA.
    Find and update persistence.xml as following in the application folder, /src/main/resources/META-INF/persistence.xml.

<persistence-unit name="Persistence_Unit_Name"> <!-- referenced in application //-->
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>PostgreSQLDS</jta-data-source> <!-- // for example of PostgreSQL datasource defined in Wildfly/JBoss -->
<properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
            <property name="hibernate.show_sql" value="false" /> <!-- Debugging purpose //-->
            <property name="hibernate.generate_statistics" value="false"/> <!-- Debugging purpose //-->
        </properties>
</persistence-unit>

If not use Connection Pool defined in Wildfly/JBoss application server, need to specify the database connection information in preperties as well.
<persistence-unit name="Persistence_Unit_Name" transaction-type="RESOURCE_LOCAL"> <!-- referenced in application //-->
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <properties>
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://server_name:5432/my_test_db" />
            <property name="javax.persistence.jdbc.user" value="db_user_name" />
            <property name="javax.persistence.jdbc.password" value="db_user_password" />

     <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
        </properties>
</persistence-unit>

Saturday, October 20, 2018

Wildfly/JBoss Application Server Setup Guide

1. Download from http://download.jboss.org/wildfly/12.0.0.Final/wildfly-12.0.0.Final.zip
   Note: Wildfly is a community version of JBoss Application Server. If you want an enterprise version, look for JBoss EAP(license required) from Redhat.

2. Install Wildfly 12.0 by unzip the binary package to a local folder, c:\wildfly-12.0.0-Final.

3. Set up environment in Windows System Environment or a batch file
     c:\> SET JBOSS_HOME=c:\wildfly-12.0.0-Final
     c:\> SET PATH=%PATH%;%JBOSS_HOME%/bin

4. Start up Wildfly/JBoss Server
     c:\wildfly-12.0.0-Final\bin> standalone.bat
     NOTE: if there is any port numbers already in use, update the port number in %JBOSS_HOME%/standalone/configuration/standalone.xml (the socket-binding section at the bottom of the configuration file)

5. Check logs in %JBOSS_HOME%/standalone/log

6. Test Wildfly/JBoss Application Server
     In web browser, access http://localhost:8080/, a default Wildfly page should show up.

7. (Hot) deploy a web application archive package (*.war file) to the Wildfly/JBoss standalone instance.
     Copy the .war file (e.g. WebDesignDemo.war) into %JBOSS_HOME%/standalone/deployments while the application server is running. The server will unpack the application into a folder with the name of the .war file.
     The application context name is normally same as the .war file name. The HTTP URL is http://localhost:8080/<Context Name> (e.g. http://localhost:8080/WebDesignDemo).
     Without specifying a page name in the URL, the server will look at the default home page, e.g. index.html or index.jsp in the application folder.
     To check if the application deployment is successful, go to the deployment folder, you should see a file with the extension of .deployed. If failed, open the file and investigate the error message.

8. Shut down Wildfly/JBoss Application Server
     Just press Ctrl+C in the application server console window, or close the MS-DOS console with the running server.
     Note: if you have any strange issues when shutting down the server, use Windows Task Manager to kill the corresponding java.exe process.

Friday, October 19, 2018

Apache Tomcat Application Server Setup Guide


1. Download from https://tomcat.apache.org/download-90.cgi

2. Install Apache Tomcat 9.x by unzip the binary package to a local folder, c:\apache-tomcat-9.0.12.

3. Set up environment in Windows System Environment or a batch file
     c:\> SET CATALINA_HOME=c:/apache-tomcat-9.0.12
     c:\> SET PATH=%PATH%;%CATALINA_HOME%/bin

4. Start up Tomcat Application Server
     c:\apache-tomcat-9.0.12\bin> startup.bat
     NOTE: if there is any port numbers already in use, update the port number in %CATALINA_HOME%/conf/server.xml

5. Check logs in %CATALINA_HOME%/logs/

6. Test Tomcat Application Server
     In web browser, access http://localhost:8080/, a default Tomcat page (physically in %CATALINA_HOME%\webapps\ROOT\index.jsp) should show up.

7. (Hot) deploy a web application archive package (*.war file)
     Copy the .war file (e.g. WebDesignDemo.war) into %CATALINA_HOME%/webapps while the application server is running. The server will unpack the application into a folder with the name of the .war file.
     The application context name is normally same as the .war file name. The HTTP URL is http://localhost:8080/<Context Name> (e.g. http://localhost:8080/WebDesignDemo).
     Without specifying a page name in the URL, the server will look at the default home page, e.g. index.html or index.jsp in the application folder.

8. Shut down Tomcat Application Server
     c:\apache-tomcat-9.0.12\bin> shutdown.bat
     Alternatively, just press Ctrl+C in the application server console window