Spring boot - Maven - build, deploy - run with dynamic classpath

Hi anh em,
Bữa trước có bài hướng dẫn xây dựng Web service với Spring boot:


Hôm nay, mình hướng dân tiếp với phần build deploy và run trên máy khác (ở đây mình dùng ubuntu)
Giả sử sau khi xây dựng xong, ta muốn đóng gói web service với cấu trúc thư mục bên dưới

classes       --chua file Jar chính mà ta đã implement
lib              -- các thư viện mà app dùng (được khai báo trong pom.xml)
logs         -- log khi start server
pid           -- chứa java proccess khi start server
properties -- config file 
scripts      -- shell script dùng để start/stop server

Cấu trúc project hiện tại

├── pom.xml
└── src
    ├── main
    │   ├── assembly
    │   │   └── assembly.xml
    │   ├── java
    │   │   └── com
    │   │       └── canh
    │   │           ├── App.java
    │   │           ├── config
    │   │           │   └── WebConfig.java
    │   │           ├── dto
    │   │           │   └── UserDto.java
    │   │           ├── services
    │   │           │   ├── ExampleServicesImpl.java
    │   │           │   └── ExampleServices.java
    │   │           └── web
    │   │               └── api
    │   │                   └── ExampleController.java
    │   ├── resources
    │   │   └── application.properties
    │   └── script
    │       ├── startServer.sh
    │       ├── startServer.sh~
    │       ├── stopServer.sh
    │       └── stopServer.sh~
    └── test
        └── java
            └── com
                └── canh
                    └── AppTest.java

Để build ra cấu trúc thư mục ở trên, ta chỉ cần config ở 2 file : pom.xml và assembly.xml
assembly.xml:

<assembly>
	<id>run_dir</id>
	<includeBaseDirectory>false</includeBaseDirectory>  
	<!-- Specifies that our binary distribution is a zip package -->
	<formats>
		<format>tar.gz</format>
	</formats>

	<!-- Adds the dependencies of our application to the lib directory -->
	<dependencySets>
		<dependencySet>
			<!-- Project artifact is not copied under library directory since it is 
				added to the root directory of the zip package. -->
			<useProjectArtifact>false</useProjectArtifact>
			<outputDirectory>lib</outputDirectory>
			<unpack>false</unpack>
		</dependencySet>
	</dependencySets>

	<fileSets>
		<!-- Adds startup scripts to the root directory of zip package. The startup 
			scripts are copied from the src/main/scripts directory. -->
		<fileSet>
			<directory>src/main/script</directory>
			<outputDirectory>script</outputDirectory>
			<includes>
				<include>*.*</include>
			</includes>
                        <fileMode>0777</fileMode>
		</fileSet>
		<!-- Adds the jar file of our example application to the root directory 
			of the created zip package. -->
		<fileSet>
			<directory>${project.build.directory}</directory>
			<outputDirectory>classes</outputDirectory>
			<includes>
				<include>*.jar</include>
			</includes>
			<excludes>
				<exclude>**/*.properties</exclude>
			</excludes>
		</fileSet>
		<fileSet>
			<directory>./</directory>
			<outputDirectory>logs</outputDirectory>
			<excludes>
				<exclude>*/**</exclude>
			</excludes>
		</fileSet>

		<fileSet>
			<directory>src/main/resources</directory>
			<outputDirectory>properties</outputDirectory>
			<includes>
				<include>*.*</include>
			</includes>
		</fileSet>

		<fileSet>
			<directory>./</directory>
			<outputDirectory>pid</outputDirectory>
			<excludes>
				<exclude>*/**</exclude>
			</excludes>
		</fileSet>
	</fileSets>
</assembly>

trong đó :

<formats>
		<format>tar.gz</format>
</formats>

là type nén: ở đây mình dùng tar.gz , nếu muốn là zip file thì sửa lai là zip.

<dependencySets>
		<dependencySet>
			<!-- Project artifact is not copied under library directory since it is 
				added to the root directory of the zip package. -->
			<useProjectArtifact>false</useProjectArtifact>
			<outputDirectory>lib</outputDirectory>
			<unpack>false</unpack>
		</dependencySet>
</dependencySets>

dùng để copy tất cả các gói jar dependency (khái báo trong pom file) vào thư mục lib

<fileSet>
			<directory>${project.build.directory}</directory>
			<outputDirectory>classes</outputDirectory>
			<includes>
				<include>*.jar</include>
			</includes>
			<excludes>
				<exclude>**/*.properties</exclude>
			</excludes>
		</fileSet>

dùng để copy file jar mà ta đã implement vào trong thư mục classes, tương tự như vậy đối với các thư mục log, pid, properties.
Tiêp theo là config trong file pom.xml

<build>
		<plugins>
			<plugin>
				<artifactId>maven-assembly-plugin</artifactId>
				<version>2.5.3</version>
				<configuration>
					<descriptor>src/main/assembly/assembly.xml</descriptor>
				</configuration>
				<executions>
					<execution>
						<id>create-archive</id>
						<phase>package</phase>
						<goals>
							<goal>single</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
</build>

trong tag build thêm plugin maven-assembly-plugin và trỏ đến đướng dẫn file assembly.xml mà bạn đã cần config. vậy là xong, khi dùng lệnh :

mvn clean install

project của ta sẽ được đóng gói trong tar.gz với cấu trúc thư mục đã định nghĩa.

JavaSpring-1.0-SNAPSHOT-run_dir.tar.gz

giờ thêm phần config clashpath là bạn sẽ có 1 standalone application để có thể chạy trên máy khác.
do máy mình dùng là unbuntu, nên mình viết trên shell script đễ config classpath, bạn nào dùng win thì tìm hiểu bat và vbs để viết script nhé.
mình viết 2 script: startServer.sh: vừa config classpath và start server, và stopServer.sh.
startServer.sh:

CLASSPATH=
for file in `ls ../lib/*.jar`
do
	CLASSPATH=${CLASSPATH}:${file}
done

for f  in `ls ../classes/*.jar`
do
	CLASSPATH=${CLASSPATH}:${f}
done
echo $CLASSPATH

java -cp ":${CLASSPATH}" \
	com.canh.App --spring.config.location=../properties/application.properties > ../logs/server.log & echo "$!" > ../pid/myjavaprogram.pid

i=0
while [ $i -lt 5 ]	
do
	
	if grep -q "Started App" ../logs/server.log;
		then
			echo "Started app"
			echo "process id:$!"
			break
  		else
    			echo "waiting..."
 	fi
	sleep 5

done

Trong java classpath được dùng để liệt kê các gói jar/thư viện để JRE tìm kiếm classes file trong đó.
đoạn script trên dùng để lấy đường dẫn file jar trong thư mục classes và lib vào 1 string, và khi run với lệnh :

java -cp ":${CLASSPATH}" \
	com.canh.App --spring.config.location=../properties/application.properties > ../logs/server.log & echo "$!" > ../pid/myjavaprogram.pid

JRE sẽ biết được các thư viện được đặt ở đây và tìm chính xác.

--spring.config.location=../properties/application.properties

khi chạy trên máy khác, có thể cần config lại port, hay thông số gì đó, ta chỉ cần chỉnh trong file này để server nhận biết.
stoppServer.sh: đơn giản là kill proccess đang chạy thôi.

#!/bin/sh
kill -9 `cat ../pid/myjavaprogram.pid`

vây là xong, giờ chỉ cần đến thư mục script run file startServer.sh

sh startServer.sh

chú ý là cần phải có set JAVA_HOME và PATH đến thư mục cài đặt java nhé.
trên unbutu có thể cài đặt cho user đang dùng như sau:

cd ~/
ls -a

lúc này sẽ thấy file .profile
thêm như bên dưới

export JAVA_HOME=/usr/lib/jvm/java-8-oracle
export PATH=$JAVA_HOME/bin:$PATH

load lại profile:

. .profile

vậy là xong. có thể run được rồi, kết qua giông như bên dưới

github: https://github.com/nguyenhuuca/JavaSpringBootExample
Chúc anh em cuối tuần vui :slight_smile:

7 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?