85 总结一下最近遇到的一些 jar发布 相关的知识

前言

呵呵 最近有一些构建服务, 发布服务的一些需求 

我们这里的服务 一般来说是 java application, spring boot application 

针对发布, 当然最好是 增量发布, 尽量的减少需要传递给 发布服务器 的资源的大小  

比如 我的这个 java application, 可能会存在很多依赖, 常规有很多种做法 

1. 吧 application 和其依赖绑定打成一个 fat jar, 然后传到发布服务器上面, java -jar $jar 

2. 通过 classpath 来关联依赖的 jar, java -classpath $classpath -jar $appJar 或者 java -classpath $classpath:$appJar $mainClazz 

假设依赖很多的话, 方式 显然会传递很多 大概率不会变化的依赖的数据, 造成一些 不必要的麻烦, 时间开销 等等 

我们这里会 介绍一些场景来尽可能的减少发布所需要的一些数据, 将一些稳定的数据 一直保存在发布服务器上面就好 

 

但是依然会存在一些问题, 希望您看完之后 有所收获 

1. 如何更加 轻量级 的发布 java application 

1. 比如 $classpath 太长的情况下, 怎么处理, 命令行支持的命令的长度是有限的, 应该怎么处理 ? 

2. spring boot 的包包括了依赖, 应该怎么 更加轻量级的发布 ? 

 

 

java application 启动的一些方式

抽象的来说又如下几种启动方式 

1. java -classpath $classpath $mainClazz

2. java -classpath $classpath -jar $jar 

 

但是我们这里要讨论如下几种情况, 当然 都可以抽象为上面两种方式, 但是 在一些特殊的场景有一些特殊的使用, 因此我们单独拎出来 

1. java -classpath $classpath $mainClazz
2. java -jar $jar
3. java  -classpath $classpath -jar $jar
4. java CommandlineRunner $classpathFile $mainClazz
5. java -jar $shortenJar $mainClazz
6. java -jar $springbootJar

 

1. java -classpath $classpath $mainClazz

这个就是我们最常见的了, 通过 -classpath 选项指定 classpath, 然后 $mainClazz 是启动类, 寻找 classpath 中的 $mainClazz, 然后调用 $mainClazz 的 main 方法 

不多赘述 

 

2. java -jar $jar

这个就是 直接通过 jar 来启动, 清单文件中包含了需要的 $classpath 和 $mainClazz 

寻找 classpath 中的 $mainClazz, 然后调用 $mainClazz 的 main 方法 

不多赘述 

 

3. java -classpath $classpath -jar $jar

在 2 的基础上增加了 指定 -classpath 

不多赘述 

 

4. java CommandlineRunner $classpathFile $mainClazz

这个就是 idea 提供的缩短 classpath 的方案之一 

通过 CommandlineRunner 代理一次, 从外部读取 classpath, 放入 classloader, 然后再 查找 $mainClazz, 调用 main 方法 

具体的实现, 请参见  idea 的 CommandlineRunner 

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTEwMzkzMzI=,size_16,color_FFFFFF,t_70

 

然后启动的方式大致如下, 抽象的来说可以归类为 java -classpath $classpath $mainClazz 

/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/bin/java -Dfile.encoding=UTF-8 com.intellij.rt.execution.CommandLineWrapper /private/var/folders/pw/lb8dvl7d6474r5plrnwtcp180000gn/T/idea_classpath880125671 com.hx.test12.Test02WechatJson

然后 $claspathFile 中的内容大致如下 

/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/jconsole.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/packager.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/sa-jdi.jar
/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/lib/tools.jar
/Users/jerry/IdeaProjects/HelloWorld/target/classes
/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar
/Users/jerry/.m2/repository/com/hx/HXLog/0.0.2/HXLog-0.0.2.jar
/Users/jerry/.m2/repository/com/hx/HXCommon/0.0.2/HXCommon-0.0.2.jar
/Users/jerry/.m2/repository/com/hx/HXJson/0.0.2/HXJson-0.0.2.jar

 

5. java -jar $shortenJar $mainClazz 

这个就是 idea 提供的缩短 classpath 的方案之一 

idea 生成一个 $classpathJar 里面通过 清单文件 指定了所有的 classpath, 然后搜索 $mainClazz, 调用 main 方法 

 

然后启动的方式大致如下, 抽象的来说可以归类为 java -classpath $classpath $mainClazz 

/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/bin/java -Dfile.encoding=UTF-8 -classpath /private/var/folders/pw/lb8dvl7d6474r5plrnwtcp180000gn/T/classpath298101813.jar com.hx.test12.Test02WechatJson

$shortenJar 里面仅仅包含一个 清单文件, 里面的 classpath 如下

注意清单文件是有规范的, 比如这里一行多少字符, 换行之后几个空格, 这些都是有约束的 

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTEwMzkzMzI=,size_16,color_FFFFFF,t_70

 

6. java -jar $springbootJar

我们做 spring boot 项目, 打包的话 可以直接生成一个可执行的 jar

直接 java -jar $springbootJar 就可以启动服务了, 非常方便 

当然 也是 spring 代理了一次, 后面将执行工作交给了我们的 XXXApplication 

里面的查询 类 spring 自己封装了一套 seach 体系 

 

 

如何更加 轻量级 的发布 java application 

呵呵 这里主要介绍一些 我们平时项目中应该怎么更加轻量级的发布 普通的 java application 

首先 打包只需要打包 我们经常变化的部分 

其他的相对比较固定的依赖 等等, 可以直接上传到服务器 我们通过 classpath 来关联 

 

$classpath 太长的情况我们应该怎么处理?, idea 提供了两种解决方式 

但是 我们这里显然是更加适合 CommandlineRunner 的处理方式, 因此 需要自己吧 CommandlineRunner dump 出来, 可能需要稍微做一些调整, 原有的 CommandlineRunner 在读取了 $classpathFile 之后就删除了该文件, 我们这里 显然是不想删除 $classpathFile 

思路 大体就这些, 接下来 分享一些 发布相关脚本  

 

总体分为三类文件, 一类是 common 脚本, 一类是应用脚本, 一类是 CommandlineRunner 所在的包 

common 这边主要是提供一些公共的服务, 比如 启动, 关闭, 备份 等等 

应用这边主要提供 启动服务, 关闭服务, 备份服务, 重启服务, 发布服务 等等 

CommandlineRunner 主要是提供 shorten $classpath 的服务 

CommandlineRunner 我们这里就不多赘述了, 自己参见 idea 的 CommandlineRunner 即可, 我这里也只是封装了一个 bootstrap/CommandlineRunner.jar 而已 

 

common/backup.sh 


echo " backup start ... "SCRIPT_DIR=$(cd $(dirname $0); pwd)
MY_BASE_DEFAULT=$(cd $SCRIPT_DIR; cd ..; pwd)
PWD=`pwd`
MY_BASE=${MY_BASE-$MY_BASE_DEFAULT}
project=$1
mainClazz=$2echo -e " \
env as follows \n \
SCRIPT_DIR : $SCRIPT_DIR \n pwd : $PWD \n MY_BASE : $MY_BASE \n project : $project \n \
mainClazz : $mainClazz \
"if [ "$project" = "" ]; thenecho " please specify project at \$1 "exit
fi
if [ "$mainClazz" = "" ]; thenecho " please specify main clazz file at \$2 "exit
fiprojectFolder=$MY_BASE/$project
if [ ! -d "$projectFolder" ]; thenecho " project folder $projectFolder does not exists, please check that "exit
fi# do business
curDate=$(date "+%Y-%m-%d")
curTime=$(date "+%H-%M-%S")bakDir="$MY_BASE/bak/$curDate/$project/$curTime"
mkdir -p "$bakDir"echo " backup project $project to $bakDir "# 1. zip dev/bak first, then transfer bak.zip 
zip -r $projectFolder/dev/bak.zip $projectFolder/dev/bak/* 
rm -rf $projectFolder/dev/bak/*
mv $projectFolder/dev/bak.zip $projectFolder/dev/bak/bak.zip
cp -r $projectFolder/dev/bak/* $bakDir/ 2>/dev/null
echo " copied /dev/bak/* in project $project to $bakDir "echo " backup end ... "

 

common/commandLineWrapperStartUp.sh


echo " startup start ... "SCRIPT_DIR=$(cd $(dirname $0); pwd)
MY_BASE_DEFAULT=$(cd $SCRIPT_DIR; cd ..; pwd)
PWD=`pwd`
MY_BASE=${MY_BASE-$MY_BASE_DEFAULT}
project=$1
ideaClasspathFile=$2
mainClazz=$3
mainArgs=$4echo -e " \
env as follows \n \
SCRIPT_DIR : $SCRIPT_DIR \n pwd : $PWD \n MY_BASE : $MY_BASE \n project : $project \n \
ideaClasspathFile : $ideaClasspathFile \n mainClazz : $mainClazz \n mainArgs : $mainArgs \
"if [ "$project" = "" ]; thenecho " please specify project at \$1 "exit
fi
if [ "$ideaClasspathFile" = "" ]; thenecho " please specify idea classpath file at \$2 "exit 
fi
if [ "$mainClazz" = "" ]; thenecho " please specify main clazz file at \$3 "exit
fiprojectFolder=$MY_BASE/$project
ideaClasspathFileFullpath=$MY_BASE/$project/$ideaClasspathFile
if [ ! -d "$projectFolder" ]; thenecho " project folder $projectFolder does not exists, please check that "exit
fi
if [ ! -f "$ideaClasspathFileFullpath" ]; thenecho " idea classpath file $ideaClasspathFileFullpath does not exists, please check that "exit
fiEXISTS_PID=`jps -lvm | grep $mainClazz | grep -v grep | awk '{print $1}'`
if [ "$EXISTS_PID" = "" ]; then
#    nohup java -cp $MY_BASE/common/Bootstrap.jar com.hx.idea.CommandLineWrapper $ideaClasspathFile $mainClazz $mainArgs > ./logs/nohup.log 2>&1 &java -jar /meiya/bootstrap/CommandLineWrapper.jar $ideaClasspathFileFullpath $mainClazz $mainArgs > logs/nohup.log  2>&1 &# tail logsleep 3sEXISTS_PID=`jps -lvm | grep $mainClazz | grep -v grep | awk '{print $1}'`echo " the main class $mainClazz startup succeed, running at $EXISTS_PID "tail -f logs/nohup.log
elseecho " the main class $mainClazz already running at $EXISTS_PID "
fiecho " startup end ... "

 

common/commandLineWrapperShutdown.sh


echo " shutdown start ... "SCRIPT_DIR=$(cd $(dirname $0); pwd)
MY_BASE_DEFAULT=$(cd $SCRIPT_DIR; cd ..; pwd)
PWD=`pwd`
MY_BASE=${MY_BASE-$MY_BASE_DEFAULT}
project=$1
mainClazz=$2echo -e " \
env as follows \n \
SCRIPT_DIR : $SCRIPT_DIR \n pwd : $PWD \n MY_BASE : $MY_BASE \n project : $project \n \
mainClazz : $mainClazz \
"if [ "$project" = "" ]; thenecho " please specify project at \$1 "exit
fi
if [ "$mainClazz" = "" ]; thenecho " please specify main clazz file at \$2 "exit
fiprojectFolder=$MY_BASE/$project
if [ ! -d "$projectFolder" ]; thenecho " project folder $projectFolder does not exists, please check that "exit
fiEXISTS_PID=`jps -lvm | grep $mainClazz | grep -v grep | awk '{print $1}'`
if [ "$EXISTS_PID" = "" ]; thenecho " the main class $mainClazz does not startup "
elsekill -9 $EXISTS_PIDecho " the main class $mainClazz shutdown succeed, kill -9 $EXISTS_PID "
fiecho " shutdown end ... "

 

 

$appHome/startUp.sh 


../common/commandLineWrapperStartUp.sh $APP_NAME ./dev/conf/gen_idea_classpath.txt org.springframework.boot.loader.JarLauncher

 

$appHome/shutdown.sh 


../common/commandLineWrapperShutdown.sh $APP_NAME org.springframework.boot.loader.JarLauncher

 

$appHome/restart.sh 


# 1. shutdown
echo ""
./shutdown.shsleep 3s# 2. startup
echo ""
./startUp.sh

 

$appHome/backup.sh 


bakDir="./dev/bak"
rm -rf $bakDir
mkdir -p $bakDirecho " prepare copy files to /dev/bak "cp *.properties $bakDir/ 2>/dev/null
echo " copied *.properties to $bakDir "
cp *.txt $bakDir/ 2>/dev/null
echo " copied *.txt to $bakDir "
cp *.jar $bakDir/ 2>/dev/null
echo " copied *.jar to $bakDir "
cp *.xml $bakDir/ 2>/dev/null
echo " copied *.xml to $bakDir "
cp *.sh $bakDir/ 2>/dev/null
echo " copied *.sh to $bakDir "cp -r resources $bakDir/ 2>/dev/null
echo " copied /resources to $bakDir "echo " copied files to /dev/bak "
echo ""
../common/backup.sh $APP_NAME org.springframework.boot.loader.JarLauncher

 

$appHome/release.sh 


mainJar="$APP_NAME-0.0.1.jar"
updatedMainJar="$APP_NAME-0.0.1.jar.0"# 0. sanity check
# ifs update for filename with blank
echo ""
SAVED_IFS=$IFS
IFS=$(echo -en "\n\b")if [ ! -f "$updatedMainJar" ]; thenecho " the newMainJar $updatedMainJar does not exists, please check that "exit
fi# 1. backup first
./backup.sh# 2. shutdown
echo ""
./shutdown.sh# 3. release jar
echo ""
rm -rf $mainJar
echo " removed oldMainJar $mainJar "
move $updatedMainJar $mainJar
echo " rebuild $mainJar succeed "sleep 3s# 4. startup
echo ""
./startUp.sh# restore ifs
IFS=$SAVED_IFS

 

新增一个项目, 需要拷贝一套 应用脚本, 然后需要调整一些配置 

        1. startUp.sh 需要调整 $APP_NAME, $classpathFIle, $mainClazz 

        2. shutdown.sh 需要调整 $APP_NAME, $mainClazz 

        3. restart.sh 不需要调整 

        4. backup.sh 需要调整 备份的业务, $APP_NAME, $mainClazz 

        5. release.sh 需要调整 $mainJar, $updatedMainJar, 发布的业务 

 

 

spring boot 的包包括了依赖, 应该怎么 更加轻量级的发布 ? 

这里的思路也很简单, 发布的服务器上面存放一份完整的 $springbootJar 的解压的版本 deflatedJar, 里面包含了 项目的 class, 配置文件, 依赖信息, spring 的代理信息 等等 

然后我们通过 maven 打包 spring boot 项目之后, 清理掉较大的 依赖 这部分 jar, 这时候 整个 jar 包就比较小了 

然后 上传到发布服务器, 然后 解压待发布的 jar, 覆盖 deflatedJar 里面的已有的内容, 然后再重新打 jar 包, 然后替换掉之前的 发布包, 然后启动服务即可 

 

主要是 release.sh 中的 发布的这一部分需要做一些调整 

# 3. release jar
echo ""
rm -rf $mainJar
echo " removed oldMainJar $mainJar "
# unzip -o -d deflatedJar/ $APP_NAME-0.0.1.jar.0
unzip -o -q -d deflatedJar/ $updatedMainJar
echo " deflated lastest jar into defaltedJar/ "
rm -rf $updatedMainJar
echo " removed updatedMainJar $updatedMainJar "
rm -rf deflatedJar/BOOT-INF/classes/application.yml
cp application.yml deflatedJar/BOOT-INF/classes/application.yml
echo " replace application.yml to BOOT-INF/classes/application.yml "
cd deflatedJar
# zip -r -0 ../$APP_NAME-0.0.1.jar .
zip -r -q -0 ../$mainJar .
cd .. 
echo " rebuild $mainJar succeed "

 

 

完 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/649455.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

探讨Go语言在构建HTTP代理时的优势和挑战

亲爱的读者,让我们一起来探讨一下Go语言在构建HTTP代理时的优势和挑战。 首先,让我们来谈谈Go语言在构建HTTP代理时的优势。Go语言是一种高性能的编程语言,它具有简洁、高效的特点,非常适合构建高效的代理服务器。使用Go语言&…

springboot第52集:微服务分布式架构,统一验证,oauth,订单,地区管理周刊

在计算机领域中,FGC 通常代表 Full Garbage Collection,即全垃圾收集。垃圾收集是一种自动管理内存的机制,它负责回收不再被程序使用的内存,以便释放资源和提高程序性能。 当系统执行 Full Garbage Collection 时,它会…

【代码随想录-数组】二分查找

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

Android源码设计模式解析与实战第2版笔记(三)

第三章 自由扩展你的项目–Builder 模式 Builder 模式的定义 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 Builder 模式的使用场景 相同的方法,不同的执行顺序,产生不同的事件结果时 多个部件或零件&…

【驱动系列】C#获取电脑硬件显卡核心代号信息

欢迎来到《小5讲堂》,大家好,我是全栈小5。 这是《驱动系列》文章,每篇文章将以博主理解的角度展开讲解, 特别是针对知识点的概念进行叙说,大部分文章将会对这些概念进行实际例子验证,以此达到加深对知识点…

msvcp140.dll丢失,有什么好的解决方法?

msvcp140.dll 是 Microsoft Visual C Redistributable Package 的一部分,这是一个由微软开发并发布的运行时库文件。具体而言: 功能与用途: msvcp140.dll 是动态链接库(DLL)文件,包含了 C 标准库的实现和…

CSS3如何实现从右往左布局的按钮组(固定间距)

可以通过下方CSS实现,下面的CSS表示按钮从右往左布局,且间距为10px: .right-btn {position: relative;float: right;margin-right: 10px; }类似这种: 这种: 注意: 不能使用right:10px代替margin-right:10px&#x…

STM32第三节——点亮第一个LED灯

1 STM32CubeMX新建工程 如果是第一次打开STM32CubeMX,软件会自动下载一些组件,等待下载完成即可。 1.2 点击ACCESS TO MCU SELECTOR 选择CPU型号,我用的是STM32F103ZET6,选择 STM32F103ZETx,可以点击旁边的收藏图标…

husky结合commitlint审查commit信息

commintlint是一个npm包用来规范化我们的commit信息,当然这个行为的操作时期是在git的commit-msg生命周期期间,这一点当然是有husky来控制,需要注意的是commit-msg作为一个git生命周期会被git commit和git merge行为唤醒,并且可以…

flutter tab页面切换练手,手势滑动、禁止滑动、page切换动画,禁止切换动画。

1&#xff1a;AppBar、TabBar、TabBarView实现页面切换&#xff0c;点击tab后tabBarView有左右切换动画&#xff0c;滑动page联动tabBar class DevicePage extends StatefulWidget {const DevicePage({super.key});overrideState<DevicePage> createState() > _Devic…

OpenGL 入门(一)— 创建窗口

文章目录 前言创建一个窗口视口动态调整输入控制渲染 完整代码 前言 关键词介绍&#xff1a; OpenGL&#xff1a; 一个定义了函数布局和输出的图形API的正式规范。GLFW&#xff1a;一个专门针对OpenGL的C语言库&#xff0c;它提供了一些渲染物体所需的最低限度的接口。它允许…

2024美赛数学建模思路 - 案例:最短时间生产计划安排

文章目录 0 赛题思路1 模型描述2 实例2.1 问题描述2.2 数学模型2.2.1 模型流程2.2.2 符号约定2.2.3 求解模型 2.3 相关代码2.4 模型求解结果 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 模型…

【doghead】2: 数据产生及pacing发送

默认采用fake的数据生产者 FakeDataProducer也可以读取h264文件生成:H264FileDataProducerUSE_FAKE_DATA_PRODUCER G:\CDN\BWE-DEV\Bifrost\worker\src\bifrost\bifrost_send_algorithm\bifrost_pacer.cpp FakeDataProducer 生产制造rtp包 ExperimentDumpData : 可用带宽、发…

【C/C++】详解程序环境和预处理(什么是程序环境?为什么要有程序环境?如何理解程序环境?)

目录 一、前言 二、 什么是程序环境&#xff1f; 三、 为什么要有程序环境&#xff1f; 四、如何理解程序环境&#xff1f; &#x1f34e; ANSI C 标准 &#x1f350; 翻译环境和执行环境 五、详解翻译环境和执行环境 &#x1f347;翻译环境&#xff08;重点&#xff01…

3ds Max宣传片怎么提升渲染速度?从硬件升级到云渲染,全面提升你的渲染速度!

在3ds Max中&#xff0c;渲染是一项耗时的任务&#xff0c;尤其是对于大型场景和复杂的动画。然而&#xff0c;通过一些优化策略和技巧&#xff0c;你可以显著加速渲染过程。以下是一些建议和技巧&#xff0c;帮助你提高3ds Max的渲染速度&#xff1a; 1.升级硬件&#xff1a; …

leetcode88合并两个有序数组

力扣&#xff08;LeetCode&#xff09;-合并两个有序数组 方法一 | 合并后排序 题目要求将两个有序数组合并并保证合并后的数组仍然有序。 观察题目可以看出&#xff0c;nums1的容量大小总是 mn&#xff0c;所以 nums2能够合并到 nums1中。 那就将 nums1中未赋值的地方赋上 …

AI Agents系列—— 探究大模型的推理能力,关于Chain-of-Thought的那些事儿

一、写在前面&#xff1a;关于AI Agents与CoT 本文是2023.07.24发表在同名公众号「陌北有棵树」上的一篇文章&#xff0c;个人观点是基础理论的学习现在仍是有必要的&#xff0c;所以搬运过来。 今天要读的论文是《Chain-of-Thought Prompting Elicits Reasoning in Large La…

ABAP SQL CDSView Entity中使用正则RegEx表达式(Regular Expressions)

1. 正则表达式测试程序 DEMO_REGEXDEMO_REGEX_TOY 2. ABAP SQL & CDSView Entity支持正则语法的场景 SQL函数语法作用执行逻辑返回类型CDS View EntitiesABAP SQLLIKE_REGEXPRLIKE_REGEXPR( PCRE pcre, VALUE sql_exp1[, CASE_SENSIT…

Ubuntu系统服务器安装宝塔面板,可一键部署幻兽帕鲁服务端且可调整游戏参数

本来腾讯云服务器&#xff08;Windows系统和Ubuntu系统&#xff09;的一键运行命令即可部署安装幻兽帕鲁功能&#xff0c;让很多多轻松搭建了自己专属的服务器&#xff0c;这也是腾讯云服务器远远甩开阿里云服务器和华为云服务器及其他品牌服务器的原因&#xff0c;没想到现在其…

ANN论文总结

本文主要是个人笔记&#xff0c;记录与存储相关的ANN工作&#xff0c;想着写都写了不如发出来与大家分享&#xff0c;大多写得比较简单有些稍微详细一点&#xff0c;内容仅供参考。 CognitiveSSD S. Liang, Y. Wang, Y. Lu, et al. Cognitive SSD: A Deep Learning Engine for…