新增 AI MCP 服务端和客户端调用,展示远程工具调用示例

This commit is contained in:
thinkgem
2025-10-19 13:30:35 +08:00
parent c7cd1d20cb
commit 3b0cc66347
32 changed files with 1094 additions and 28 deletions

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
*/
package com.jeesite.modules;
import com.jeesite.common.config.Global;
import com.jeesite.common.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
* Application
* @author ThinkGem
*/
@SpringBootApplication
public class AiMcpApplication extends SpringBootServletInitializer {
private static final Logger logger = LoggerFactory.getLogger(AiMcpApplication.class);
public static void main(String[] args) {
SpringApplication.run(AiMcpApplication.class, args);
logger.info(
"\n\n==============================================================\n"
+ "\n 启动完成MCP 地址http://127.0.0.1:{}/api/v1/sse\n"
+ "\n==============================================================\n",
Global.getProperty("server.port") + FileUtils.path(
Global.getProperty("server.servlet.context-path")));
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
this.setRegisterErrorPageFilter(false); // 错误页面有容器来处理而不是SpringBoot
return builder.sources(AiMcpApplication.class);
}
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
*/
package com.jeesite.modules.ai.mcp.config;
import com.jeesite.modules.ai.tools.TestAiTools;
import com.jeesite.modules.ai.tools.UserAITools;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
/**
* AI MCP 服务配置
* @author ThinkGem
*/
@Lazy(false)
@Configuration
public class McpServerConfig {
@Bean
public ToolCallbackProvider mcpServerTools(TestAiTools testAiTools, UserAITools userAITools) {
return MethodToolCallbackProvider.builder().toolObjects(testAiTools, userAITools).build();
}
}

View File

@@ -0,0 +1,53 @@
# 使用环境配置,只需 JVM 参数里加:-Dspring.profiles.active=prod
#======================================#
#========== Server settings ===========#
#======================================#
server:
port: 8980
servlet:
context-path: /js
#======================================#
#========= Database settings ==========#
#======================================#
# 数据库连接
jdbc:
# Mysql 数据库配置
type: mysql
driver: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.56.1:3306/jeesite?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=Asia/Shanghai
username: jeesite
password: jeesite
testSql: SELECT 1
# 数据库连接池配置
pool:
# 初始化连接数
init: 1
# 最小连接数
minIdle: 3
# 最大连接数
maxActive: 20
#======================================#
#========== Spring settings ===========#
#======================================#
# 日志配置
logging:
config: classpath:config/logback-spring-prod.xml
# MyBatis 相关
mybatis:
# Mapper文件刷新线程
mapper:
refresh:
enabled: false

View File

@@ -0,0 +1,171 @@
#======================================#
#========== Project settings ==========#
#======================================#
# 产品或项目名称、软件开发公司名称
productName: JeeSite MCP Server
companyName: ThinkGem
# 产品版本、版权年份
productVersion: V5.14
copyrightYear: 2025
# 是否演示模式
demoMode: false
# 专为分离端提供接口服务
apiMode: false
#======================================#
#========== Server settings ===========#
#======================================#
server:
port: 8981
# servlet:
# context-path: /js
# register-default-servlet: false
# tomcat:
# uri-encoding: UTF-8
# max-http-form-post-size: 20MB
#
# # 当 Nginx 为 httpstomcat 为 http 时,设置该选项为 true
# schemeHttps: false
#======================================#
#========= Database settings ==========#
#======================================#
# 数据库连接
jdbc:
# Mysql 数据库配置
type: mysql
driver: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/jeesite_v5?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=Asia/Shanghai
username: root
password: 123456
testSql: SELECT 1
# 数据库连接池配置
pool:
# 初始化连接数
init: 1
# 最小连接数
minIdle: 3
# 最大连接数
maxActive: 20
#======================================#
#========== Framework settings ========#
#======================================#
spring:
# 应用程序名称
application:
name: jeesite-web-ai-mcp
# 环境名称(注意:不可设置为 test 它是单元测试专用的名称)
profiles:
active: default
# 打印横幅
main:
banner-mode: "off"
lazy-initialization: false
# # MVC 映射匹配策略
# mvc:
# pathmatch:
# matching-strategy: ANT_PATH_MATCHER
# JTA XA 事务spring boot 3
jta:
enabled: false
# 缓存配置
cache:
# 缓存及会话共享(专业版)
isClusterMode: false
# ========= MPC Server 相关配置 =========
# https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html
ai:
mcp:
server:
enabled: true
name: jeesite-mcp-server
version: 1.0.0
type: SYNC
sse-endpoint: /api/v1/sse
sse-message-endpoint: /api/v1/mcp
capabilities:
tool: true
resource: true
prompt: true
completion: true
# 日志配置
logging:
config: classpath:config/logback-spring.xml
# MyBatis 相关
mybatis:
# Mapper文件刷新线程
mapper:
refresh:
enabled: false
# 管理基础路径
adminPath: /a
# 前端基础路径
frontPath: /f
# 基础配置(参数、模块、字典)
config:
enabled: true
# 用户权限相关(用户、角色、菜单)
user:
enabled: true
# 国际化管理
lang:
enabled: false
# 任务调度
job:
enabled: false
# 代码生成
gen:
enabled: false
# 系统监控(默认开启,可关闭)访问地址如下:
# 服务监控http://127.0.0.1:8980/js/a/state/server/index
state:
enabled: true
# 核心功能 Controller 开关
web:
core:
enabled: true
# 文件上传
file:
enabled: false
# 消息提醒中心
msg:
enabled: false
#======================================#
#========== Project settings ==========#
#======================================#

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="false">
<!-- Log file path -->
<property name="log.path" value="${logPath:-${java.io.tmpdir:-.}}/logs" />
<!-- Framework level setting -->
<include resource="config/logger-default.xml" />
<!-- Production level setting -->
<logger name="org.mybatis.spring.transaction" level="INFO" />
<logger name="org.flowable.ui.modeler.domain" level="INFO" />
<logger name="org.flowable.idm.engine.impl.persistence" level="INFO" />
<logger name="org.flowable.task.service.impl.persistence" level="INFO" />
<logger name="org.flowable.identitylink.service.impl.persistence" level="INFO" />
<logger name="org.flowable.variable.service.impl.persistence" level="INFO" />
<logger name="org.flowable.engine.impl.persistence" level="INFO" />
<logger name="com.bstek.ureport" level="INFO" />
<logger name="com.jeesite.modules" level="INFO" />
<!-- Project level setting -->
<!-- <logger name="your.package" level="DEBUG" /> -->
<logger name="io.modelcontextprotocol" level="INFO" />
<logger name="org.springframework.ai" level="INFO" />
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{MM-dd HH:mm:ss.SSS} %clr(%-5p) %clr([%-39logger{39}]){cyan} - %m%n%wEx</pattern>
</encoder>
</appender>
<!-- Log file debug output -->
<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/debug.%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p ${PID:- } [%15.15t] [%-39logger{39}] [%X{TRACE_ID}] - %m%n%wEx</pattern>
</encoder>
<!--<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>DENY</onMatch>
<onMismatch>NEUTRAL</onMismatch>
</filter>-->
</appender>
<!-- Log file error output -->
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p ${PID:- } [%15.15t] [%-39logger{39}] [%X{TRACE_ID}] - %m%n%wEx</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="WARN">
<appender-ref ref="console" />
<appender-ref ref="debug" />
<appender-ref ref="error" />
</root>
</configuration>

View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="false">
<!-- Log file path -->
<property name="log.path" value="${logPath:-${java.io.tmpdir:-.}}/logs" />
<!-- Framework level setting -->
<include resource="config/logger-core.xml" />
<!-- Project level setting -->
<!-- <logger name="your.package" level="DEBUG" /> -->
<logger name="io.modelcontextprotocol" level="TRACE" />
<logger name="org.springframework.ai" level="TRACE" />
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{MM-dd HH:mm:ss.SSS} %clr(%-5p) %clr([%-39logger{39}]){cyan} - %m%n%wEx</pattern>
</encoder>
</appender>
<!-- Log file debug output -->
<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/debug.%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p ${PID:- } [%15.15t] [%-39logger{39}] [%X{TRACE_ID}] - %m%n%wEx</pattern>
</encoder>
<!--<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>DENY</onMatch>
<onMismatch>NEUTRAL</onMismatch>
</filter>-->
</appender>
<!-- Log file error output -->
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p ${PID:- } [%15.15t] [%-39logger{39}] [%X{TRACE_ID}] - %m%n%wEx</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="WARN">
<appender-ref ref="console" />
<appender-ref ref="debug" />
<appender-ref ref="error" />
</root>
</configuration>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -0,0 +1,23 @@
<%/* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law. */ %>
<link rel="stylesheet" href="${ctxStatic}/fonts/font-icons.min.css">
<link rel="stylesheet" href="${ctxStatic}/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="${ctxStatic}/select2/4.0/select2.css?${_version}">
<link rel="stylesheet" href="${ctxStatic}/icheck/1.0/minimal/grey.css?${_version}">
<% if (@ListUtils.inString('zTree', libs!)){ %>
<link rel="stylesheet" href="${ctxStatic}/jquery-ztree/3.5/css/awesome/zTreeStyle.css?${_version}">
<% } %>
<% if (@ListUtils.inString('tabPage', libs!)){ %>
<link rel="stylesheet" href="${ctxStatic}/wdScrollTab/css/TabPanel.css?${_version}">
<% } %>
<% if (@ListUtils.inString('dataGrid', libs!)){ %>
<link rel="stylesheet" href="${ctxStatic}/jqGrid/4.7/css/ui.jqgrid.css?${_version}">
<% } %>
<% if (@ListUtils.inString('layout', libs!)){ %>
<link rel="stylesheet" href="${ctxStatic}/jquery-plugins/jquery.layout-latest.css?${_version}">
<% } %>
<% if (@ListUtils.inString('fileupload', libs!)){ %>
<link rel="stylesheet" href="${ctxStatic}/webuploader/0.1/webuploader.extend.css?${_version}">
<% } %>
<link rel="stylesheet" href="${ctxStatic}/adminlte/css/AdminLTE.min.css?${_version}">
<link rel="stylesheet" href="${ctxStatic}/common/jeesite.css?${_version}">

View File

@@ -0,0 +1,11 @@
<%/* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law. */%>
<meta charset="utf-8"><meta content="webkit" name="renderer"/><meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="keywords" content="PoweredByJeeSiteV4.0"/><meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"/>
<meta name="description" content="PoweredByJeeSiteV4.0"/><meta content="no-cache" http-equiv="Pragma"/><meta http-equiv="Expires" content="0"/>
<meta content="width=device-width, initial-scale=1, user-scalable=1" name="viewport"/>
<title>${(isNotBlank(title!) ? title! + ' - ' : '') + @Global.getConfig('productName')}</title>
<link rel="shortcut icon" href="${ctxStatic}/favicon.png" type="image/png">
<script src="${ctxPath}/global.min.js?ctx=${ctx}"></script>
<script src="${ctxStatic}/jquery/jquery-3.7.0.min.js"></script>
<script src="${ctxStatic}/jquery/jquery-migrate-3.4.0.min.js"></script>

View File

@@ -0,0 +1,48 @@
<%/* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law. */ %>
<script src="${ctxStatic}/bootstrap/js/bootstrap.min.js"></script>
<script src="${ctxStatic}/select2/4.0/select2.js?${_version}"></script>
<script src="${ctxStatic}/select2/4.0/i18n/${lang()}.js?${_version}"></script>
<script src="${ctxStatic}/layer/3.5/layer.js?${_version}"></script>
<script src="${ctxStatic}/laydate/5.3/laydate.js?${_version}"></script>
<% if (@ListUtils.inString('zTree', libs!)){ %>
<script src="${ctxStatic}/jquery-ztree/3.5/js/jquery.ztree.all-3.5.js?${_version}"></script>
<% } %>
<% if (@ListUtils.inString(['tabPage', 'dataGrid', 'fileupload'], libs!)){ %>
<script src="${ctxStatic}/jquery/jquery-ui-sortable-1.13.2.min.js"></script>
<% } %>
<% if (@ListUtils.inString('tabPage', libs!)){ %>
<script src="${ctxStatic}/wdScrollTab/js/TabPanel.js?${_version}"></script>
<script src="${ctxStatic}/wdScrollTab/js/TabPanel.extend.js?${_version}"></script>
<script src="${ctxStatic}/wdScrollTab/js/TabPanel_i18n.js?${_version}"></script>
<% } %>
<% if (@ListUtils.inString('dataGrid', libs!)){ %>
<script src="${ctxStatic}/jqGrid/4.7/js/jquery.jqGrid.js?${_version}"></script>
<script src="${ctxStatic}/jqGrid/4.7/js/jquery.jqGrid.extend.js?${_version}"></script>
<script src="${ctxStatic}/jqGrid/4.7/js/i18n/${lang()}.js?${_version}"></script>
<% } %>
<% if (@ListUtils.inString('validate', libs!)){ %>
<script src="${ctxStatic}/jquery-validation/1.16/jquery.validate.js?${_version}"></script>
<script src="${ctxStatic}/jquery-validation/1.16/localization/messages_${lang()}.js?${_version}"></script>
<script src="${ctxStatic}/jquery-validation/1.16/jquery.validate.extend.js?${_version}"></script>
<% } %>
<% if (@ListUtils.inString('layout', libs!)){ %>
<script src="${ctxStatic}/jquery/jquery-ui-draggable-1.13.2.min.js?${_version}"></script>
<script src="${ctxStatic}/jquery/jquery-ui-effect-1.13.2.min.js?${_version}"></script>
<script src="${ctxStatic}/jquery-plugins/jquery.layout-latest.js?${_version}"></script>
<% } %>
<% if (@ListUtils.inString('inputmask', libs!)){ %>
<script src="${ctxStatic}/jquery-plugins/jquery.inputmask.js?${_version}"></script>
<% } %>
<% if (@ListUtils.inString('fileupload', libs!)){ %>
<script src="${ctxStatic}/webuploader/0.1/webuploader.js?${_version}"></script>
<script src="${ctxStatic}/webuploader/0.1/webuploader.extend.js?${_version}"></script>
<script src="${ctxStatic}/webuploader/0.1/i18n/${lang()}.js?${_version}"></script>
<% } %>
<% if (@ListUtils.inString('ueditor', libs!)){ %>
<script src="${ctxStatic}/ueditor/1.4/ueditor.config.js?${_version}"></script>
<script src="${ctxStatic}/ueditor/1.4/ueditor.all.js?${_version}"></script>
<script src="${ctxStatic}/ueditor/1.4/lang/${lang()}/${lang()}.js?${_version}"></script>
<% } %>
<script src="${ctxStatic}/common/jeesite.js?${_version}"></script>
<!--<script src="${ctxStatic}/common/i18n/jeesite_${lang()}.js?${_version}"></script>-->

View File

@@ -0,0 +1,12 @@
<%/* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law. */ %>
<% print('<'+'!DOC'+'TYPE html'+'>'); %>
<% print('<'+'html'+'><'+'head'+'>'); %>
<% include('/include/head.html', {title: text(title!)}){} %>
<% include('/include/csslibs.html', {libs: libs!}){} %>
</head><body class="hold-transition ${bodyClass!}">
<% if (!@ListUtils.inString('layout', libs!)){ %>
<div class="wrapper">${layoutContent}</div>
<% }else{ %>${layoutContent}<% } %>
<a id="scroll-up" href="#" class="btn btn-sm hide"><i class="fa fa-angle-double-up"></i></a>
<% include('/include/jslibs.html', {libs: libs!}){} %>

View File

@@ -0,0 +1,51 @@
@echo off
rem /**
rem * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
rem * No deletion without permission, or be held responsible to law.
rem *
rem * Author: ThinkGem@163.com
rem */
echo.
echo [<5B><>Ϣ] <20><><EFBFBD><EFBFBD>Web<65><62><EFBFBD>̡<EFBFBD>
echo.
rem pause
rem echo.
%~d0
cd %~dp0
title %cd%
rem <20><><EFBFBD><EFBFBD>JDKĿ¼
rem set "JAVA_HOME=%cd%\jdk1.8.0_x64"
rem <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>
set "CLASS_PATH=%cd%/../"
rem <20>Ż<EFBFBD>JVM<56><4D><EFBFBD><EFBFBD>
set "JAVA_OPTS=%JAVA_OPTS% -Xms512m -Xmx1024m"
rem <20><>ʽһ<CABD><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ⲿ<EFBFBD>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
rem set "JAVA_OPTS=%JAVA_OPTS% -Dspring.config.location=%cd%\app.yml"
rem <20><>ʽ<EFBFBD><CABD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><C3BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƣ<EFBFBD><C6A3><EFBFBD><EFBFBD>ز<EFBFBD>ͬ<EFBFBD><CDAC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>
rem set "JAVA_OPTS=%JAVA_OPTS% -Dspring.profiles.active=prod"
if "%JAVA_HOME%" == "" goto noJavaHome
if not "%JAVA_HOME%" == "" goto gotJavaHome
goto end
:noJavaHome
set RUN_JAVA=java
goto runJava
:gotJavaHome
set "RUN_JAVA=%JAVA_HOME%\bin\java"
goto runJava
:runJava
call "%RUN_JAVA%" -cp %CLASS_PATH% %JAVA_OPTS% org.springframework.boot.loader.launch.WarLauncher %*
goto end
:end
pause

View File

@@ -0,0 +1,35 @@
#!/bin/sh
# /**
# * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
# * No deletion without permission, or be held responsible to law.
# *
# * Author: ThinkGem@163.com
# */
echo ""
echo "[信息] 运行Web工程。"
echo ""
cd "$(cd "$(dirname "$0")"; pwd)"
# 设置JDK目录
# JAVA_HOME="$PWD/jdk1.8.0_x64"
# 设置类加载路径
CLASS_PATH="$PWD/../"
# 优化JVM参数
# JAVA_OPTS="$JAVA_OPTS -Xms512m -Xmx1024m"
# 方式一、配置外部自定义的属性文件(建议)
# JAVA_OPTS="$JAVA_OPTS -Dspring.config.location=$PWD/app.yml"
# 方式二、配置环境名称,加载不同的属性文件
# JAVA_OPTS="$JAVA_OPTS -Dspring.profiles.active=prod"
if [ -z "$JAVA_HOME" ]; then
RUN_JAVA=java
else
RUN_JAVA="$JAVA_HOME"/bin/java
fi
exec "$RUN_JAVA" -cp $CLASS_PATH $JAVA_OPTS org.springframework.boot.loader.launch.WarLauncher $@