What's?
I wanted to pretend to be Nablarch for a moment.
I would like to set a little theme and try it from the beginning.
Reference) Nablarch 5u18 has been released!
This environment is here.
$ java --version
openjdk 11.0.9 2020-10-20
OpenJDK Runtime Environment (build 11.0.9+11-Ubuntu-0ubuntu1.20.04)
OpenJDK 64-Bit Server VM (build 11.0.9+11-Ubuntu-0ubuntu1.20.04, mixed mode, sharing)
$ mvn --version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 11.0.9, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.4.0-52-generic", arch: "amd64", family: "unix"
$ docker version
Client: Docker Engine - Community
Version: 19.03.13
API version: 1.40
Go version: go1.13.15
Git commit: 4484c46d9d
Built: Wed Sep 16 17:02:52 2020
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 19.03.13
API version: 1.40 (minimum version 1.12)
Go version: go1.13.15
Git commit: 4484c46d9d
Built: Wed Sep 16 17:01:20 2020
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.3.7
GitCommit: 8fba4e9a7d01810a393d5d25a3621dc101981175
runc:
Version: 1.0.0-rc10
GitCommit: dc9208a3303feef5b3839f4323d9beb36df0a9dd
docker-init:
Version: 0.18.0
GitCommit: fec3683
Take a look at Getting Started.
If you want to create a full-fledged application, create it from a blank project.
It seems better to start with a blank project, so let's make it a blank project.
Starting with Nablarch 5u18, support for containers is included, so use this. You can create a Docker image with Jib.
Setup procedure.
Initial setup of RESTful web service project for container
I have a batch file for Windows, but since this environment is Linux, I execute maven-archetype-plugin: 2.4: generate
directly.
$ mvn org.apache.maven.plugins:maven-archetype-plugin:2.4:generate \
-DinteractiveMode=false \
-DarchetypeGroupId=com.nablarch.archetype \
-DarchetypeArtifactId=nablarch-container-jaxrs-archetype \
-DarchetypeVersion=5u18 \
-DgroupId=com.example \
-DartifactId=hello-nablarch-restful \
-Dversion=0.0.1 \
-Dpackage=com.example
It's done.
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: nablarch-container-jaxrs-archetype:5u18
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.example
[INFO] Parameter: artifactId, Value: hello-nablarch-restful
[INFO] Parameter: version, Value: 0.0.1
[INFO] Parameter: package, Value: com.example
[INFO] Parameter: packageInPathFormat, Value: com/example
[INFO] Parameter: package, Value: com.example
[INFO] Parameter: groupId, Value: com.example
[INFO] Parameter: artifactId, Value: hello-nablarch-restful
[INFO] Parameter: version, Value: 0.0.1
[INFO] project created from Archetype in dir: /path/to/hello-nablarch-restful
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.583 s
[INFO] Finished at: 2020-11-11T11:10:34+09:00
[INFO] ------------------------------------------------------------------------
Moved into the project.
$ cd hello-nablarch-restful
Let's take a quick look at the file.
$ find -type f
./README.md
./pom.xml
./.gitignore
./h2/bin/h2-1.3.176.jar
./h2/bin/h2w.bat
./h2/bin/h2.sh
./h2/bin/h2.bat
./h2/db/SAMPLE.h2.db
./h2/db/SAMPLE.h2.db.org
./db/ddl/db2/create.sql
./db/ddl/db2/drop.sql
./db/ddl/sqlserver/create.sql
./db/ddl/sqlserver/drop.sql
./db/ddl/oracle/create.sql
./db/ddl/oracle/drop.sql
./db/ddl/postgresql/create.sql
./db/ddl/postgresql/drop.sql
./db/ddl/h2/create.sql
./db/ddl/h2/drop.sql
./db/data/db2/data.sql
./db/data/sqlserver/data.sql
./db/data/oracle/data.sql
./db/data/postgresql/data.sql
./db/data/h2/data.sql
./src/main/webapp/WEB-INF/web.xml
./src/main/resources/entity/data-model_sqlserver.edm
./src/main/resources/entity/data-model.edm
./src/main/resources/entity/data-model_postgresql.edm
./src/main/resources/entity/data-model_db2.edm
./src/main/resources/entity/data-model_oracle.edm
./src/main/resources/rest-boot.xml
./src/main/resources/env.config
./src/main/resources/META-INF/services/nablarch.core.repository.di.config.externalize.ExternalizedComponentDefinitionLoader
./src/main/resources/rest-component-configuration.xml
./src/main/resources/messages.properties
./src/main/resources/data-source.xml
./src/main/resources/log.properties
./src/main/resources/app-log.properties
./src/main/resources/common.config
./src/main/jib/usr/local/tomcat/conf/server.xml
./src/main/jib/usr/local/tomcat/conf/logging.properties
./src/main/java/com/example/entity/package-info.java
./src/main/java/com/example/entity/SampleUser.java
./src/main/java/com/example/package-info.java
./src/main/java/com/example/domain/package-info.java
./src/main/java/com/example/domain/SampleDomainBean.java
./src/main/java/com/example/domain/SampleDomainManager.java
./src/main/java/com/example/SampleAction.java
./src/main/java/com/example/dto/SampleUserListDto.java
./src/test/resources/log.properties
./src/test/resources/unit-test.xml
./src/test/resources/data/SAMPLE_USER.csv
./src/test/java/com/example/SampleApiTest.java
./src/test/java/com/example/SampleActionTest.java
The configuration is explained here.
Maven Archetype Configuration (https://nablarch.github.io/docs/5u18/doc/application_framework/application_framework/blank_project/MavenModuleStructures/index.html)
Let's take a look at the profile.
$ mvn help:all-profiles
[INFO] Scanning for projects...
[WARNING] The POM for org.eclipse.m2e:lifecycle-mapping:jar:1.0.0 is missing, no dependency information available
[WARNING] Failed to retrieve plugin descriptor for org.eclipse.m2e:lifecycle-mapping:1.0.0: Plugin org.eclipse.m2e:lifecycle-mapping:1.0.0 or one of its dependencies could not be resolved: Failure to find org.eclipse.m2e:lifecycle-mapping:jar:1.0.0 in https://repo.maven.apache.org/maven2 was cached in the local repository, resolution will not be reattempted until the update interval of central has elapsed or updates are forced
[INFO]
[INFO] -----------------< com.example:hello-nablarch-restful >-----------------
[INFO] Building hello-nablarch-restful 0.0.1
[INFO] --------------------------------[ war ]---------------------------------
[WARNING] The POM for org.eclipse.m2e:lifecycle-mapping:jar:1.0.0 is missing, no dependency information available
[WARNING] Failed to retrieve plugin descriptor for org.eclipse.m2e:lifecycle-mapping:1.0.0: Plugin org.eclipse.m2e:lifecycle-mapping:1.0.0 or one of its dependencies could not be resolved: Failure to find org.eclipse.m2e:lifecycle-mapping:jar:1.0.0 in https://repo.maven.apache.org/maven2 was cached in the local repository, resolution will not be reattempted until the update interval of central has elapsed or updates are forced
[INFO]
[INFO] --- maven-help-plugin:3.2.0:all-profiles (default-cli) @ hello-nablarch-restful ---
[INFO] Listing Profiles for Project: com.example:hello-nablarch-restful:war:0.0.1
Profile Id: BACKUP (Active: false , Source: pom)
Profile Id: gsp (Active: false , Source: pom)
Profile Id: ossrh (Active: false , Source: pom)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.106 s
[INFO] Finished at: 2020-11-11T11:06:10+09:00
[INFO] ------------------------------------------------------------------------
Archetypes for containers don't seem to have the concept of profiles for the environment.
Container production environment settings
It seems that the value written in env.config
is set to be overwritten by the environment variable.
When creating a project, it looks like this.
$ grep -vE '^$|^#' src/main/resources/env.config
nablarch.db.jdbcDriver=org.h2.Driver
nablarch.db.url=jdbc:h2:./h2/db/SAMPLE
nablarch.db.user=SAMPLE
nablarch.db.password=SAMPLE
nablarch.db.schema=PUBLIC
nablarch.db.maxPoolSize=5
nablarch.db.minimumIdle=5
nablarch.db.connectionTimeout=30000
nablarch.db.idleTimeout=600000
nablarch.db.maxLifetime=1800000
nablarch.db.validationTimeout=5000
nablarch.codeCache.loadOnStartUp=false
There seems to be a sample Action class as well
src/main/java/com/example/SampleAction.java
package com.example;
import com.example.dto.SampleUserListDto;
import com.example.entity.SampleUser;
import nablarch.common.dao.EntityList;
import nablarch.common.dao.UniversalDao;
import nablarch.fw.web.HttpRequest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
/**
*Action class for communication confirmation.
*
* @deprecated TODO This is a class for checking communication. Please delete it after the confirmation is completed.
*/
@Path("/find")
public class SampleAction {
/**
*Search process.
* <p>
*Use JSON for the response.
* </p>
*
* @param req HTTP request
* @return user information(JSON)
*/
@GET
@Path("/json")
@Produces(MediaType.APPLICATION_JSON)
public EntityList<SampleUser> findProducesJson(HttpRequest req) {
return UniversalDao.findAll(SampleUser.class);
}
/**
*Search process.
* <p>
*Use XML for the response.
* </p>
*
* @param req HTTP request
* @return user information(XML)
*/
@GET
@Path("/xml")
@Produces(MediaType.APPLICATION_XML)
public SampleUserListDto findProducesXml(HttpRequest req) {
EntityList<SampleUser> sampleUserList = UniversalDao.findAll(SampleUser.class);
return new SampleUserListDto(sampleUserList);
}
}
For the time being, let's follow the method of communication confirmation.
First, create a Docker image.
$ mvn package jib:dockerBuild -DskipTests=true
I was able to do it.
$ docker image ls | grep hello
hello-nablarch-restful 0.0.1 a4b772a979de 50 years ago 448MB
hello-nablarch-restful latest a4b772a979de 50 years ago 448MB
The default base image is that of Tomcat.
I will start it.
$ docker container run -it --rm -p 8080:8080 -v `pwd`/h2:/usr/local/tomcat/h2 hello-nablarch-restful:0.0.1
Confirmation. It seems OK for the time being.
$ curl localhost:8080/find/json
[{"userId":1,"kanjiName":"Rakutaro Nabe","kanaName":"Naburakutaro"},{"userId":2,"kanjiName":"Rakujiro Nabe","kanaName":"Naburakujiro"}]
By the way, if you forget to specify Volume in this procedure, the database will disappear and you will fail (I was addicted to overlooking it).
-v `pwd`/h2:/usr/local/tomcat/h2
XML-Less DI Configuration
Next, let's use DI lightly. It seems that DI using annotations can be done, so I will use this.
Build an object of annotated class
It seems to create a subclass of the AnnotationComponentDefinitionLoader
class.
What's the place to put it? I thought, so refer to the following.
Service Development Reference for SPA + REST API Configuration
Well, as a result I kept it simple.
src/main/java/com/example/system/HelloRestComponentDefinitionLoader.java
package com.example.system;
import nablarch.core.repository.di.config.externalize.AnnotationComponentDefinitionLoader;
public class HelloRestComponentDefinitionLoader extends AnnotationComponentDefinitionLoader {
@Override
protected String getBasePackage() {
return "com.example";
}
}
The getBasePackage
method returns the package to be scanned.
I'm incorporating this into the ServiceLoader gimmick, but it seems that the target file already exists.
src/main/resources/META-INF/services/nablarch.core.repository.di.config.externalize.ExternalizedComponentDefinitionLoader
nablarch.core.repository.di.config.externalize.OsEnvironmentVariableExternalizedLoader
nablarch.core.repository.di.config.externalize.SystemPropertyExternalizedLoader
Added here.
nablarch.core.repository.di.config.externalize.OsEnvironmentVariableExternalizedLoader
nablarch.core.repository.di.config.externalize.SystemPropertyExternalizedLoader
com.example.system.HelloRestComponentDefinitionLoader
Create a class that was in the sample and transferred the data acquisition process. Add the @SystemRepositoryComponent
annotation.
src/main/java/com/example/service/UserService.java
package com.example.service;
import com.example.entity.SampleUser;
import nablarch.common.dao.EntityList;
import nablarch.common.dao.UniversalDao;
import nablarch.core.repository.di.config.externalize.annotation.SystemRepositoryComponent;
@SystemRepositoryComponent
public class UserService {
public EntityList<SampleUser> findUsers() {
return UniversalDao.findAll(SampleUser.class);
}
}
So, create an Action class that uses this.
src/main/java/com/example/HelloAction.java
package com.example;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.example.entity.SampleUser;
import com.example.service.UserService;
import nablarch.common.dao.EntityList;
import nablarch.core.repository.di.config.externalize.annotation.SystemRepositoryComponent;
@SystemRepositoryComponent
@Path("/hello")
public class HelloAction {
UserService userService;
public HelloAction(UserService userService) {
this.userService = userService;
}
@GET
@Path("/json")
@Produces(MediaType.APPLICATION_JSON)
public EntityList<SampleUser> users() {
return userService.findUsers();
}
}
In the constructor, let's receive the component.
public HelloAction(UserService userService) {
this.userService = userService;
}
It seems that additional settings are required to manage the Action class in a DI container.
Manage Action class with DI container
This is an excerpt.
src/main/resources/rest-component-configuration.xml
<!--Package mapping settings-->
<component name="packageMapping" class="nablarch.integration.router.PathOptionsProviderRoutesMapping">
<property name="pathOptionsProvider">
<component class="nablarch.integration.router.jaxrs.JaxRsPathOptionsProvider">
<property name="applicationPath" value="${nablarch.webApi.applicationPath}"/>
<property name="basePackage" value="${nablarch.commonProperty.basePackage}"/>
</component>
</property>
<property name="methodBinderFactory">
<component class="nablarch.fw.jaxrs.JaxRsMethodBinderFactory">
<property name="handlerList">
<component class="nablarch.integration.jaxrs.jersey.JerseyJaxRsHandlerListFactory"/>
</property>
</component>
</property>
</component>
Add SystemRepositoryDelegateFactory
as delegateFactory
.
<!--Package mapping settings-->
<component name="packageMapping" class="nablarch.integration.router.PathOptionsProviderRoutesMapping">
<property name="pathOptionsProvider">
<component class="nablarch.integration.router.jaxrs.JaxRsPathOptionsProvider">
<property name="applicationPath" value="${nablarch.webApi.applicationPath}"/>
<property name="basePackage" value="${nablarch.commonProperty.basePackage}"/>
</component>
</property>
<property name="methodBinderFactory">
<component class="nablarch.fw.jaxrs.JaxRsMethodBinderFactory">
<property name="handlerList">
<component class="nablarch.integration.jaxrs.jersey.JerseyJaxRsHandlerListFactory"/>
</property>
</component>
</property>
<property name="delegateFactory">
<component class="nablarch.fw.handler.SystemRepositoryDelegateFactory"/>
</property>
</component>
After doing so, build the Docker image again and start it.
$ mvn package jib:dockerBuild -DskipTests=true
$ docker container run -it --rm -p 8080:8080 -v `pwd`/h2:/usr/local/tomcat/h2 hello-nablarch-restful:0.0.1
Confirmation.
$ curl localhost:8080/hello/json
[{"userId":1,"kanjiName":"Rakutaro Nabe","kanaName":"Naburakutaro"},{"userId":2,"kanjiName":"Rakujiro Nabe","kanaName":"Naburakujiro"}]
It's OK.
Finally, let's change the database used from H2 to PostgreSQL.
Procedure for changing RDBMS to be used
The user and schema used for connection must be created in the RDBMS.
Because it means that.
PostgreSQL decides to use the Docker image.
Even if it is 12.2 or higher, it will be okay ...
Looking at the structure of the project, it seems that it contains SQL files
Maven Archetype Configuration (https://nablarch.github.io/docs/5u18/doc/application_framework/application_framework/blank_project/MavenModuleStructures/index.html)
Convert to UTF-8 (written in Shift_JIS ...).
$ iconv -f sjis -t utf-8 db/ddl/postgresql/create.sql -o db/ddl/postgresql/create.sql.utf8
$ iconv -f sjis -t utf-8 db/data/postgresql/data.sql -o db/data/postgresql/data.sql.utf8
Start PostgreSQL.
$ docker container run -it --rm --name postgres \
-v `pwd`/db:/data/db \
-e POSTGRES_USER=postgres_user \
-e POSTGRES_PASSWORD=postgres_password \
-e POSTGRES_DB=mydatabase \
postgres:13.0
Enter the container, execute DDL and register data.
$ docker container exec -it postgres bash
$ psql -U postgres_user -f /data/db/ddl/postgresql/create.sql.utf8 mydatabase
$ psql -U postgres_user -f /data/db/data/postgresql/data.sql.utf8 mydatabase
This time, let's treat it like another host without binding it to the port on the host side. Check the IP address of the container.
$ docker container inspect postgres | grep IPAddress
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.2",
"IPAddress": "172.17.0.2",
Replaced JDBC driver from H2 to PostgreSQL
<!-- TODO:Modify the JDBC driver according to the DB product used in the project.-->
<!--
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.176</version>
<scope>runtime</scope>
</dependency>
-->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.18</version>
<scope>runtime</scope>
</dependency>
Fixed connection settings. You can overwrite it with an environment variable, but this time.
src/main/resources/env.config
$ grep nablarch.db src/main/resources/env.config
#nablarch.db.jdbcDriver=org.h2.Driver
nablarch.db.jdbcDriver=org.postgresql.Driver
#nablarch.db.url=jdbc:h2:./h2/db/SAMPLE
nablarch.db.url=jdbc:postgresql://172.17.0.2:5432/mydatabase
#nablarch.db.user=SAMPLE
nablarch.db.user=postgres_user
#nablarch.db.password=SAMPLE
nablarch.db.password=postgres_password
#nablarch.db.schema=PUBLIC
nablarch.db.schema=public
Creating a container image.
$ mvn clean
$ mvn package jib:dockerBuild -DskipTests=true
Start-up.
$ docker container run -it --rm -p 8080:8080 hello-nablarch-restful:0.0.1
Confirmation.
$ curl localhost:8080/hello/json
][{"userId":1,"kanjiName":"Rakutaro Nabe","kanaName":"Naburakutaro"},{"userId":2,"kanjiName":"Rakujiro Nabe","kanaName":"Naburakujiro"}]
I was able to do it.