diff --git a/.gitignore b/.gitignore
index 499bd94ad24e509499bde09c3abf4f8c3044e749..fe3e44c14fde858ba13d1dcdb6afc6f64fa9ddd5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,25 +1,757 @@
-.cache
-scalastyle-output.xml
-.classpath
-.idea
-.idea/**
+.scratch/
+/vote-db-boot/src/main/resources/static/
+.mvn/repository
+.jpb
+
+# Created by https://www.gitignore.io/api/vim,node,java,linux,macos,emacs,nanoc,eclipse,windows,java-web,visualstudio,jetbrains+iml,visualstudiocode,maven,gradle
+
+### Eclipse ###
+
.metadata
-.settings
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.settings/
+.loadpath
+.recommenders
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# PyDev specific (Python IDE for Eclipse)
+*.pydevproject
+
+# CDT-specific (C/C++ Development Tooling)
+.cproject
+
+# CDT- autotools
+.autotools
+
+# Java annotation processor (APT)
+.factorypath
+
+# PDT-specific (PHP Development Tools)
+.buildpath
+
+# sbteclipse plugin
+.target
+
+# Tern plugin
+.tern-project
+
+# TeXlipse plugin
+.texlipse
+
+# STS (Spring Tool Suite)
+.springBeans
+
+# Code Recommenders
+.recommenders/
+
+# Annotation Processing
+.apt_generated/
+
+# Scala IDE specific (Scala & Java development for Eclipse)
+.cache-main
+.scala_dependencies
+.worksheet
+
+### Eclipse Patch ###
+# Eclipse Core
.project
-.version.properties
-filter.properties
-logs.zip
-/**/target
-target
-tmp
+
+# JDT-specific (Eclipse Java Development Tools)
+.classpath
+
+# Annotation Processing
+.apt_generated
+
+### Emacs ###
+# -*- mode: gitignore; -*-
+*~
+\#*\#
+/.emacs.desktop
+/.emacs.desktop.lock
+*.elc
+auto-save-list
+tramp
+.\#*
+
+# Org-mode
+.org-id-locations
+*_archive
+
+# flymake-mode
+*_flymake.*
+
+# eshell files
+/eshell/history
+/eshell/lastdir
+
+# elpa packages
+/elpa/
+
+# reftex files
+*.rel
+
+# AUCTeX auto folder
+/auto/
+
+# cask packages
+.cask/
+dist/
+
+# Flycheck
+flycheck_*.el
+
+# server auth directory
+/server/
+
+# projectiles files
+.projectile
+
+# directory configuration
+.dir-locals.el
+
+### Java ###
+# Compiled class file
*.class
-*.iml
-*.swp
-*.jar
+
+# Log file
*.log
-.DS_Store
-build-target
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+# *.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+### Java-Web ###
+## ignoring target file
+target/
+
+### JetBrains+iml ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+### JetBrains+iml Patch ###
+# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
+
+*.iml
+modules.xml
+.idea/misc.xml
*.ipr
-*.iws
+### Linux ###
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+### macOS ###
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### Maven ###
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+dependency-reduced-pom.xml
+buildNumber.properties
+.mvn/timing.properties
+.mvn/wrapper/maven-wrapper.jar
+
+### Nanoc ###
+# For projects using Nanoc (http://nanoc.ws/)
+
+# Default location for output (needs to match output_dir's value found in nanoc.yaml)
+output/
+
+# Temporary file directory
+tmp/nanoc/
+
+# Crash Log
+crash.log
+
+### Node ###
+# Logs
+logs
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# next.js build output
+.next
+
+# nuxt.js build output
+.nuxt
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless
+
+### Vim ###
+# Swap
+[._]*.s[a-v][a-z]
+[._]*.sw[a-p]
+[._]s[a-rt-v][a-z]
+[._]ss[a-gi-z]
+[._]sw[a-p]
+
+# Session
+Session.vim
+
+# Temporary
+.netrwhist
+# Auto-generated tag files
+tags
+# Persistent undo
+[._]*.un~
+
+### VisualStudioCode ###
+.vscode/*
+# !.vscode/settings.json
+# !.vscode/tasks.json
+# !.vscode/launch.json
+# !.vscode/extensions.json
+
+### Windows ###
+# Windows thumbnail cache files
+Thumbs.db
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+### Gradle ###
+.gradle
+/build/
+
+# Ignore Gradle GUI config
+gradle-app.setting
+
+# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
+!gradle-wrapper.jar
+
+# Cache of project
+.gradletasknamecache
+
+# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
+# gradle/wrapper/gradle-wrapper.properties
+
+### VisualStudio ###
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp_proj
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+
+# End of https://www.gitignore.io/api/vim,node,java,linux,macos,emacs,nanoc,eclipse,windows,java-web,visualstudio,jetbrains+iml,visualstudiocode,maven,gradle
+
+!charts/*/charts/*.tgz
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..de4fb832027ae6d59072888e54e9b12bac32ba6b
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,45 @@
+include:
+ - https://repos.iec.io/gitlab-ci/template/experimental/.standard-maven-project.yml
+
+
+
+variables:
+ # Java
+ JAVA_DOCKER_IMAGE: registry.inspures.com/public/docker.io/library/eclipse-temurin:8-jdk
+ # Maven
+ ALLOW_PUBLISHING_TO_MAVEN_REPO: "true"
+ POSTGRES_PASSWORD: postgres
+
+ # Review
+ ALLOW_UPGRADE_REVIEW_ENVIRONMENT: "true"
+ ALLOW_REMOVE_REVIEW_ENVIRONMENT: "false"
+
+.services-for-maven-tests:
+ services:
+ # - name: redis:latest
+ # alias: redis
+ - name: postgres:11
+ alias: postgres
+
+.refs-to-review:
+ only:
+ refs:
+ - main
+# - 42-pipeline
+
+# .refs-to-publish-snapshot:
+# only:
+# refs:
+# - main
+
+# .refs-to-publish-release:
+# only:
+# refs:
+# - tags
+
+
+.refs-to-publish:
+ only:
+ refs:
+ - main
+ - tags
\ No newline at end of file
diff --git a/.mvn/settings.xml b/.mvn/settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c1a2a190d4d8746e64fb54bca6df8b338a9698dd
--- /dev/null
+++ b/.mvn/settings.xml
@@ -0,0 +1,113 @@
+
+ .mvn/repository
+
+
+ caf-repo
+ ${env.ARTIFACTS_REPO_USERNAME}
+ ${env.ARTIFACTS_REPO_PASSWORD}
+
+
+ caf-snapshots-repo
+ ${env.ARTIFACTS_REPO_USERNAME}
+ ${env.ARTIFACTS_REPO_PASSWORD}
+
+
+ caf-releases-repo
+ ${env.ARTIFACTS_REPO_USERNAME}
+ ${env.ARTIFACTS_REPO_PASSWORD}
+
+
+ gsp-repo
+ ${env.ARTIFACTS_REPO_USERNAME}
+ ${env.ARTIFACTS_REPO_PASSWORD}
+
+
+ gsp-snapshots-repo
+ ${env.ARTIFACTS_REPO_USERNAME}
+ ${env.ARTIFACTS_REPO_PASSWORD}
+
+
+ gsp-releases-repo
+ ${env.ARTIFACTS_REPO_USERNAME}
+ ${env.ARTIFACTS_REPO_PASSWORD}
+
+
+
+
+ gsp-repo
+ gsp-rep
+ gsp-repo
+ https://repos.iec.io/repository/maven-gsp/
+
+
+ gsp-releases-repo
+ gsp-rep
+ gsp-releases-repo
+ https://repos.iec.io/repository/maven-gsp-releases/
+
+
+ gsp-snapshots-repo
+ gsp-rep
+ gsp-snapshots
+ https://repos.iec.io/repository/maven-gsp-snapshots/
+
+
+
+
+ caf-repo
+ caf-rep
+ https://repos.iec.io/repository/maven-caf/
+
+
+ caf-releases-repo
+ caf-rep
+ caf-releases-repo
+ https://repos.iec.io/repository/maven-caf-releases/
+
+
+ caf-snapshots-repo
+ caf-rep
+ caf-snapshots-repo
+ https://repos.iec.io/repository/maven-caf-snapshots/
+
+
+
+
+ repos.iec.io-caf
+
+
+ caf-rep
+ https://repos.iec.io/repository/maven-caf/
+
+ true
+
+
+ true
+ always
+
+
+
+
+
+ repos.iec.io-gsp
+
+
+ gsp-rep
+ https://repos.iec.io/repository/maven-gsp/
+
+ true
+
+
+ true
+ always
+
+
+
+
+
+
+
+ repos.iec.io-caf
+ repos.iec.io-gsp
+
+
diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 0000000000000000000000000000000000000000..c788ef118992410ccceffa90ace2ec5cb4796c15
--- /dev/null
+++ b/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2007-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ private static final String WRAPPER_VERSION = "0.5.5";
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if(mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if(mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if(!outputFile.getParentFile().exists()) {
+ if(!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+ String username = System.getenv("MVNW_USERNAME");
+ char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+ Authenticator.setDefault(new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+ }
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
\ No newline at end of file
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000000000000000000000000000000000000..7882bbb72688b28388f7abf9e59ebf33bdc95c2b
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repos.iec.io/repository/maven-public/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
+wrapperUrl=https://repos.iec.io/repository/maven-public/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/mvnw b/mvnw
new file mode 100755
index 0000000000000000000000000000000000000000..41056d43f63a1e4537e8abfe61837444f49b6706
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
\ No newline at end of file
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 0000000000000000000000000000000000000000..32bde6aedce3b46eaec0a6086a7414499f705858
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 2b8b678445398d0c8ea37bc44974978e24bdf590..c7a3957da3509ab442c33ce4cd7ff63988dab430 100644
--- a/pom.xml
+++ b/pom.xml
@@ -27,14 +27,86 @@
io.iec.edp
caf-boot-parent
- 0.3.7
+ 2.0.0-rc.2
rest-api-engine
com.inspur.edp
pom
- 1.0.2
+ 1.0.3-SNAPSHOT
+
+
+
+
+ org.apache.commons
+ commons-lang3
+ 3.9
+
+
+ commons-io
+ commons-io
+ 2.16.1
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.5.2
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.5.2
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.5.2
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ 5.5.2
+
+
+ org.junit.platform
+ junit-platform-commons
+ 1.5.2
+
+
+ org.junit.platform
+ junit-platform-engine
+ 1.5.2
+
+
+
+
+ org.mockito
+ mockito-core
+ 3.1.0
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 3.1.0
+
+
+
+ org.powermock
+ powermock-api-mockito2
+ 2.0.9
+ test
+
+
+
+
+
+ scm:git:ssh://git@git.iec.io:6060/gsp-cloud-ds/cdp/jit.git
+ scm:git:ssh://git@git.iec.io:6060/gsp-cloud-ds/cdp/jit.git
+ https://git.iec.io/gsp-cloud-ds/cdp/jit
+ HEAD
+
@@ -46,6 +118,11 @@
false
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.22.2
+
@@ -59,5 +136,4 @@
https://repos.iec.io/repository/maven-gsp-releases/
-
diff --git a/rest-api-jit-api/pom.xml b/rest-api-jit-api/pom.xml
index f2a2950be9997f7a957237ff4c35a147579cf4a0..5f8ba4ef383ad223bc9e8b620191a60b73010490 100644
--- a/rest-api-jit-api/pom.xml
+++ b/rest-api-jit-api/pom.xml
@@ -21,13 +21,12 @@
rest-api-engine
com.inspur.edp
- 1.0.2
+ 1.0.3-SNAPSHOT
4.0.0
rest-api-jit-api
-
@@ -46,4 +45,4 @@
-
\ No newline at end of file
+
diff --git a/rest-api-jit-core/pom.xml b/rest-api-jit-core/pom.xml
index 651671e221f49534ba7dbf0ff0f106158b7873ff..d7d78d0b8aceb41a66f64b9a982dbe645e0aecab 100644
--- a/rest-api-jit-core/pom.xml
+++ b/rest-api-jit-core/pom.xml
@@ -21,7 +21,7 @@
rest-api-engine
com.inspur.edp
- 1.0.2
+ 1.0.3-SNAPSHOT
4.0.0
@@ -29,41 +29,35 @@
io.iec.edp
- caf-boot-commons-environment
+ caf-boot-starter-base
com.inspur.edp
rest-api-jit-api
${project.version}
- compile
-
-
- commons-io
- commons-io
- 2.6
-
-
- org.slf4j
- slf4j-api
org.apache.commons
commons-lang3
- org.springframework.boot
- spring-boot
-
-
- io.iec.edp
- caf-boot-commons-json
+ commons-io
+ commons-io
com.inspur.edp
rest-api-jit-spi
${project.version}
- compile
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+
+
+ org.powermock
+ powermock-api-mockito2
+ test
-
\ No newline at end of file
+
diff --git a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/common/DirectoryManager.java b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/common/DirectoryManager.java
index 24069d6cbd872a3613296ad6c5583ccd6dabf7e1..73c65288f133ce26c6f77f61ef2114305697ccb9 100644
--- a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/common/DirectoryManager.java
+++ b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/common/DirectoryManager.java
@@ -17,6 +17,7 @@
package com.inspur.edp.jit.core.common;
import io.iec.edp.caf.common.environment.EnvironmentUtil;
+import io.iec.edp.caf.commons.runtime.CafEnvironment;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -27,22 +28,25 @@ import java.util.List;
@Slf4j
public class DirectoryManager {
- private static final String BOOT_LIB_DIRECTORY_NAME = "bootlibs";
- private static String JIT_BASE_DIR = "var" + File.separator + "jit";
+ private static final String JIT_DIR = "jit";
+ private static final String JIT_BASE_DIR;
private static String HOME_DIR = null;
- private static Object lockObj = new Object();
-
- public static String getUnZipBootJarDirectory() {
- return getHomeDir() + File.separator + JIT_BASE_DIR + File.separator + BOOT_LIB_DIRECTORY_NAME;
+ private static final Object lockObj = new Object();
+
+ static {
+ String deployUnitName = CafEnvironment.getCurrentDeployunitName();
+ String jitBaseDir = "var" + File.separator;
+ if (StringUtils.isBlank(deployUnitName)) {
+ JIT_BASE_DIR = jitBaseDir + JIT_DIR;
+ } else {
+ // 同一份安装盘会同时启动多个进程来加载多个程序,通过deployUnitName区分不同进程,保证每个进程读写自己的文件
+ JIT_BASE_DIR = jitBaseDir + deployUnitName + File.separator + JIT_DIR;
+ }
}
public static String getHomeDir() {
if (HOME_DIR == null) {
- String cafHome = EnvironmentUtil.getServerRTPath();
- if (cafHome == null || cafHome.length() == 0) {
- throw new RuntimeException("获取server目录异常,获取的值为null");
- }
- HOME_DIR = cafHome;
+ HOME_DIR = EnvironmentUtil.getServerRTPath();
}
return HOME_DIR;
}
@@ -59,12 +63,8 @@ public class DirectoryManager {
return getJitDirectory(functionType) + File.separator + functionId;
}
- public static void removeFunctionDir(String functionType, String functionId) {
- String dir = getJitDirectory(functionType, functionId);
- File file = new File(dir);
- if (file.exists()) {
- file.delete();
- }
+ public static String get3rdDirectory() {
+ return getHomeDir() + File.separator + "runtime" + File.separator + "3rd";
}
/**
@@ -103,22 +103,12 @@ public class DirectoryManager {
return result;
}
- public static void clean() {
- String dir = getJitBaseDirectory();
- File file = new File(dir);
- if (file.exists()) {
- file.delete();
- }
- }
-
-
public static boolean createDir(String creatingDir) {
File file = new File(creatingDir);
if (!file.exists()) {
synchronized (lockObj) {
if (!file.exists()) {
- file.mkdirs();
- return true;
+ return file.mkdirs();
}
}
}
diff --git a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/common/StartManager.java b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/common/StartManager.java
index 2f82ad322bc2421915eef3ae64550b1fdedff8f7..4bbdf73daa7bd9e1cf2fdef11f790717fdcb1e8f 100644
--- a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/common/StartManager.java
+++ b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/common/StartManager.java
@@ -32,8 +32,6 @@ public class StartManager implements ApplicationListener {
if (event instanceof ApplicationStartedEvent) {
String jitBaseDirectory = DirectoryManager.getJitBaseDirectory();
DirectoryManager.createDir(jitBaseDirectory);
- String bootLibsDirectory = DirectoryManager.getUnZipBootJarDirectory();
- DirectoryManager.createDir(bootLibsDirectory);
ApplicationContext applicationContext = ((ApplicationStartedEvent) event).getApplicationContext();
Map eventMap = applicationContext.getBeansOfType(StartEvent.class);
diff --git a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/common/UnZipManager.java b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/common/UnZipManager.java
deleted file mode 100644
index 0d393a6d0093d02b9553f6bccada874c3e63b9d7..0000000000000000000000000000000000000000
--- a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/common/UnZipManager.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.inspur.edp.jit.core.common;
-
-import org.apache.commons.io.IOUtils;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-
-public class UnZipManager {
-
- private static Object lockObj = new Object();
- private static List UNZIPED_LIST = new ArrayList<>();
-
- public static List unZip(List jarNames) throws IOException {
- List realyJars = new ArrayList<>();
- List needJars = getNeedJars(jarNames, realyJars);
- if (needJars.size() == 0) {
- return realyJars;
- }
- synchronized (lockObj) {
- JarFile jar = new JarFile(getBootJarDirectory());
- Enumeration entryEnumeration = jar.entries();
- List entries = new ArrayList<>();
- while (entryEnumeration.hasMoreElements()) {
- entries.add(entryEnumeration.nextElement());
- }
- final String entryLibPrefix = "BOOT-INF/lib/";
- String bootlibs = DirectoryManager.getUnZipBootJarDirectory();
-
- for (String jarPrefixName : needJars) {
- String entryNamePrefix = entryLibPrefix + jarPrefixName;
- boolean isFind = false;
- for (JarEntry entry : entries) {
- String entryName = entry.getName();
- if (entryName.startsWith(entryNamePrefix) && entryName.endsWith(".jar")) {
- //找到后保存jar包
- String jarFileName = entryName.substring(entryLibPrefix.length());
- File jarFile = new File(bootlibs, jarFileName);
- if (!jarFile.exists()) {
- try {
- FileOutputStream fos = new FileOutputStream(jarFile);
- IOUtils.copy(jar.getInputStream(entry), fos);
- fos.close();
- } catch (Exception ex) {
- throw new RuntimeException("spring boot unzip error:", ex);
- }
- //增加到缓存
- UNZIPED_LIST.add(entryName);
- }
- realyJars.add(jarFileName);
- isFind = true;
- }
- }
- if (!isFind) {
- throw new RuntimeException("SpringBoot无法找到【" + jarPrefixName + "】");
- }
- }
- }
- return realyJars;
- }
-
- /**
- * 得到未缓存的jar包。已经存在的jar包路径存入入参realyNames中。
- *
- * @param jarNames 所有需要的jar包列表
- * @param realyNames 已经存在的jar包路径。在方法中被填入值
- * @return 未缓存的jar包名称
- */
- private static List getNeedJars(List jarNames, List realyNames) {
- List needJars = new ArrayList<>();
- for (String jarName : jarNames) {
- boolean unZiped = false;
- for (String unZipName : UNZIPED_LIST) {
- if (unZipName.toLowerCase().startsWith(jarName.toLowerCase())) {
- unZiped = true;
- realyNames.add(unZipName);
- //此处不break,避免引用具有相同前缀的错误的jar包
- }
- }
- if (!unZiped) {
- needJars.add(jarName);
- }
- }
- return needJars;
- }
-
- public static String getBootJarDirectory() {
- return DirectoryManager.getHomeDir() + File.separator + "runtime" + File.separator + "caf-bootstrap.jar";
- }
-
-}
diff --git a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/BytesJavaFileObject.java b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/BytesJavaFileObject.java
new file mode 100644
index 0000000000000000000000000000000000000000..e24bc6b20409c5aac1e1c618e8954acf5c9154cd
--- /dev/null
+++ b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/BytesJavaFileObject.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.inspur.edp.jit.core.compile;
+
+import javax.tools.SimpleJavaFileObject;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * @author Kaixuan Shi
+ * @since 2023/8/24
+ */
+public class BytesJavaFileObject extends SimpleJavaFileObject {
+ private final String className;
+ private ByteArrayOutputStream classBytesOs;
+
+ public BytesJavaFileObject(String className, Kind kind) {
+ super(fromClassName(className), kind);
+ this.className = className;
+ }
+
+ private static URI fromClassName(String className) {
+ try {
+ return new URI(className);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(className, e);
+ }
+ }
+
+ /**
+ * 编译结果回调的OutputStream,回调成功后通过getClassBytes()方法获取目标类编译后的字节码字节数组
+ */
+ @Override
+ public OutputStream openOutputStream() {
+ return classBytesOs = new ByteArrayOutputStream();
+ }
+
+ public byte[] getClassBytes() {
+ return classBytesOs.toByteArray();
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+}
diff --git a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/CompileServiceImpl.java b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/CompileServiceImpl.java
index 1e5b82245b5a22ccf5a2a81e4d8be02455fb5f8a..06d150f5f77e665d6d189ba6a22b0489f7f1cebe 100644
--- a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/CompileServiceImpl.java
+++ b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/CompileServiceImpl.java
@@ -20,8 +20,9 @@ import com.inspur.edp.jit.api.compile.CompileContext;
import com.inspur.edp.jit.api.compile.CompileService;
import com.inspur.edp.jit.api.compile.JavaSourceCode;
import com.inspur.edp.jit.core.common.DirectoryManager;
-import com.inspur.edp.jit.core.common.UnZipManager;
import com.inspur.edp.jit.core.load.ClassLoadManager;
+import com.inspur.edp.jit.core.util.JarUtil;
+import com.inspur.edp.jit.core.util.JitIOException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
@@ -38,15 +39,17 @@ import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class CompileServiceImpl implements CompileService {
- private static Logger logger = LoggerFactory.getLogger(CompileServiceImpl.class);
- private JavaCompiler compiler;
+ private static final Logger logger = LoggerFactory.getLogger(CompileServiceImpl.class);
+ private final JavaCompiler compiler;
public CompileServiceImpl() {
logger.info("new CompileServiceImpl");
@@ -59,152 +62,82 @@ public class CompileServiceImpl implements CompileService {
//构造StringJavaFileObject
List javaFileObjects = new ArrayList<>();
- context.getSourceCodes().forEach(sourceCode -> {
- javaFileObjects.add(new StringJavaFileObject(sourceCode.getFullClassName(), sourceCode.getSourceCode()));
- });
-
- //创建目录
- String destDir = getOutDirPath(context);
- DirectoryManager.createDir(destDir);
+ context.getSourceCodes().forEach(
+ sourceCode ->
+ javaFileObjects.add(
+ new StringJavaFileObject(sourceCode.getFullClassName(), sourceCode.getSourceCode())
+ )
+ );
//编译
- try {
- compile(destDir, referJars, javaFileObjects);
- } catch (Exception e) {
- //编译出错时移除空文件夹
- //如果之前存在编译结果,则不移除
- deleteEmptyDirWithoutThrows(destDir);
- throw e;
- }
+ JitJavaFileManager javaFileManager = compile(referJars, javaFileObjects);
- //更新manifest.json
- ManifestUtil.updateManifest(context);
+ //获取编译结果
+ List compiledClasses = getJavaFileObject(context, javaFileManager);
+ //更新ClassLoader
+ ClassLoadManager.put(context.getFunctionType(), context.getFunctionId(), compiledClasses);
- //卸载ClassLoader
- ClassLoadManager.unLoad(context.getFunctionType(), context.getFunctionId());
+ //更新功能包缓存
+ updateFunctionCache(context, compiledClasses);
+ }
+
+ /**
+ * 更新功能包缓存
+ */
+ private void updateFunctionCache(CompileContext context, List compiledClasses) {
+ String functionDir = DirectoryManager.getJitDirectory(context.getFunctionType(), context.getFunctionId());
+ DirectoryManager.createDir(functionDir);
+ //更新jar包
+ JarUtil.write(functionDir, context.getFunctionId(), compiledClasses);
+ //更新Manifest文件
+ ManifestUtil.updateManifest(context);
}
@Override
public void batchCompile(List contexts) {
//按照类名分组,类名不同的分在同一组,相同的分在不同组
- List> compileListGroup = buildCompileGroups(contexts);
+ List> compileGroups = buildCompileGroups(contexts);
- logger.info("bat compile {} split to {} groups", contexts.size(), compileListGroup.size());
- compileListGroup.forEach(group -> {
- //编译到默认位置(第一个compileContext目录中)
- CompileContext firstContext = group.get(0);
- String destDir = getOutDirPath(firstContext);
- DirectoryManager.createDir(destDir);
+ logger.info("bat compile {} split to {} groups", contexts.size(), compileGroups.size());
+ compileGroups.forEach(group -> {
//合并依赖
List referJars = mergeAndParseClassPathList(group);
//合并java文件
- List javaFileObjects = new ArrayList<>();
- group.forEach(context -> {
- context.getSourceCodes().forEach(sourceCode -> {
- javaFileObjects.add(new StringJavaFileObject(sourceCode.getFullClassName(), sourceCode.getSourceCode()));
- });
- });
+ List javaFileObjects = new ArrayList<>(group.size());
+ group.forEach(context ->
+ context.getSourceCodes().forEach(sourceCode ->
+ javaFileObjects.add(
+ new StringJavaFileObject(sourceCode.getFullClassName(), sourceCode.getSourceCode())
+ )
+ )
+ );
//批量编译
- try {
- compile(destDir, referJars, javaFileObjects);
- } catch (Exception e) {
- //编译出错时移除空文件夹
- //如果之前存在编译结果,则不移除
- deleteEmptyDirWithoutThrows(destDir);
- throw e;
- }
-
- //处理除第一个外的所有CompileContext
- //提取文件,将编译后的类从默认位置转移到功能包位置
- for (int i = 1; i < group.size(); i++) {
- CompileContext context = group.get(i);
- context.getSourceCodes().forEach(javaSourceCode -> {
- String oldDirPath = getJavaSourceCodeCompileDir(firstContext, javaSourceCode);
- String newDirPath = getJavaSourceCodeCompileDir(context, javaSourceCode);
-
- File oldDir = new File(oldDirPath);
- File newDir = new File(newDirPath);
-
- String simpleClassName = getSimpleClassName(javaSourceCode);
- String classFileName = simpleClassName + ".class";
- String innerClassPrefixFileName = simpleClassName + "$";
+ JitJavaFileManager javaFileManager = compile(referJars, javaFileObjects);
- if (!oldDir.exists()) {
- logger.warn("目标路径【{}】不存在", oldDirPath);
- return;
- }
- File[] files = oldDir.listFiles();
- for (File file : files) {
- if (file.isDirectory()) {
- continue;
- }
-
- String fileName = file.getName();
- if (fileName.equals(classFileName) || fileName.startsWith(innerClassPrefixFileName)) {
- //转移类本身及其内部类
- try {
- //复制到目标位置
- File newFile = new File(newDir, file.getName());
- if (newFile.exists()) {
- newFile.delete();
- }
- FileUtils.moveFile(file, newFile);
- } catch (IOException e) {
- throw new RuntimeException(e.getMessage(), e);
- }
- }
- }
- recursiveDeleteEmptyDirWithoutThrows(oldDir);
- });
- }
+ group.forEach(context -> {
+ List compiledClasses = getJavaFileObject(context, javaFileManager);
+ //更新classLoader
+ ClassLoadManager.put(context.getFunctionType(), context.getFunctionId(), compiledClasses);
+ //更新功能包缓存
+ updateFunctionCache(context, compiledClasses);
+ });
});
-
- //更新manifest.json
- contexts.forEach(ManifestUtil::updateManifest);
-
- //卸载classLoader
- contexts.forEach(context -> ClassLoadManager.unLoad(context.getFunctionType(), context.getFunctionId()));
- }
-
- private void deleteEmptyDirWithoutThrows(String dirPath) {
- try {
- File dir = new File(dirPath);
- String[] list = dir.list();
- if (list != null && list.length == 0) {
- dir.delete();
- }
- } catch (Exception e) {
- logger.error("删除文件夹【" + dirPath + "】出错:" + e.getMessage(), e);
- }
}
/**
- * 递归删除空文件夹
+ * 从JitJavaFileManager中获取编译后的字节码
*/
- private void recursiveDeleteEmptyDirWithoutThrows(File dir) {
- try {
- String[] list = dir.list();
- if (list != null && list.length == 0) {
- File parentFile = dir.getParentFile();
- dir.delete();
- recursiveDeleteEmptyDirWithoutThrows(parentFile);
- }
- } catch (Exception e) {
- logger.error("删除文件夹【" + dir.getAbsolutePath() + "】出错:" + e.getMessage(), e);
- }
- }
+ private List getJavaFileObject(CompileContext context, JitJavaFileManager javaFileManager) {
- private String getSimpleClassName(JavaSourceCode javaSourceCode) {
- String fullClassName = javaSourceCode.getFullClassName();
- int index = fullClassName.lastIndexOf(".");
- if (index >= 0) {
- return fullClassName.substring(index + 1);
- } else {
- return fullClassName;
- }
+ List javaFileObjects = new ArrayList<>();
+ context.getSourceCodes().forEach(javaSourceCode -> {
+ //内部类会被编译成多个class字节码,所以需要addAll
+ javaFileObjects.addAll(javaFileManager.getCompiledJavaFileObject(javaSourceCode.getFullClassName()));
+ });
+ return javaFileObjects;
}
/**
@@ -226,6 +159,9 @@ public class CompileServiceImpl implements CompileService {
return parseClassPathList(new ArrayList<>(referBootSet), new ArrayList<>(referSet));
}
+ /**
+ * 将编译上下文里的类按照类名分组,类名不同的分在同一组,相同的分在不同组
+ */
private List> buildCompileGroups(List contexts) {
List> classNameSetGroup = new ArrayList<>();
List> compileListGroup = new ArrayList<>();
@@ -266,43 +202,6 @@ public class CompileServiceImpl implements CompileService {
return compileListGroup;
}
- /**
- * 得到在编译上下文中,指定的Java源文件的编译结果class文件的最终存放目录。
- *
- * @param context 编译上下文
- * @param javaSourceCode 编译上下文
- * @return class文件实际存放目录
- */
- private String getJavaSourceCodeCompileDir(CompileContext context, JavaSourceCode javaSourceCode) {
- String packageName = getPackage(javaSourceCode);
- String packagePath = packageName.replace(".", File.separator);
- String rootPath = DirectoryManager.getJitDirectory(context.getFunctionType(), context.getFunctionId());
- return rootPath + File.separator + packagePath;
- }
-
- /**
- * 得到源文件的包名
- *
- * @param javaSourceCode java类源文件
- * @return 包名
- */
- private String getPackage(JavaSourceCode javaSourceCode) {
- String fullClassName = javaSourceCode.getFullClassName();
- int index = fullClassName.lastIndexOf('.');
- String packageName = fullClassName.substring(0, index);
- return packageName;
- }
-
- /**
- * 得到编译上下文的编译结果存放目录
- *
- * @param context 编译上下文
- * @return 结果存放目录
- */
- private String getOutDirPath(CompileContext context) {
- return DirectoryManager.getJitDirectory(context.getFunctionType(), context.getFunctionId());
- }
-
/**
* 解析所有的依赖jar包,将其转换为jar包的绝对路径列表。
*
@@ -312,29 +211,18 @@ public class CompileServiceImpl implements CompileService {
private List parseClassPathList(CompileContext context) {
List refers = context.getRefers();
List referBoots = context.getReferBoots();
- List referJars = parseClassPathList(referBoots, refers);
- return referJars;
+ return parseClassPathList(referBoots, refers);
}
/**
* 解析所有的依赖jar包,将其转换为jar包的绝对路径列表。
- *
- * @param referBoots
- * @param refers
- * @return
*/
private List parseClassPathList(List referBoots, List refers) {
List referJars = new ArrayList<>();
//jar包解压
if (referBoots != null && referBoots.size() > 0) {
- try {
- List zips = UnZipManager.unZip(referBoots);
- zips.forEach(name -> {
- referJars.add(DirectoryManager.getUnZipBootJarDirectory() + File.separator + name);
- });
- } catch (Exception ex) {
- throw new RuntimeException("编译时解压boot包出错:", ex);
- }
+ //从3rd目录下寻找第三方jar包
+ referJars.addAll(findIn3rdDir(referBoots));
}
if (refers != null && refers.size() > 0) {
refers.forEach(name -> {
@@ -344,32 +232,67 @@ public class CompileServiceImpl implements CompileService {
return referJars;
}
- public void compile(String destDir, List refers, List extends JavaFileObject> javaFiles) {
+ /**
+ * 从3rd中找到依赖的jar并返回绝对路径
+ */
+ private Collection findIn3rdDir(List referBoots) {
+ List refers = new ArrayList<>(referBoots.size());
+ String thirdDir = DirectoryManager.get3rdDirectory();
+ Collection jarFiles = FileUtils.listFiles(new File(thirdDir), new String[]{"jar"}, false);
+ List refersBootsCopy = new ArrayList<>(referBoots);
+ for (File file : jarFiles) {
+ if (refersBootsCopy.isEmpty()) {
+ break;
+ }
+ Iterator iterator = refersBootsCopy.iterator();
+ while (iterator.hasNext()) {
+ String referBoot = iterator.next();
+ if (file.getName().startsWith(referBoot)) {
+ refers.add(thirdDir + File.separator + file.getName());
+ iterator.remove();
+ break;
+ }
+ }
+ }
+ if (!refersBootsCopy.isEmpty()) {
+ StringBuilder builder = new StringBuilder("未在目录中找到依赖的第三方jar包:");
+ builder.append(thirdDir);
+ for (String refer : referBoots) {
+ builder.append(",").append(refer);
+ }
+ throw new JitCompileException(builder.toString());
+ }
+ return refers;
+ }
+
+ private JitJavaFileManager compile(List refers, List extends JavaFileObject> javaFiles) {
DiagnosticCollector diagnosticCollector = new DiagnosticCollector<>();
- StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnosticCollector, null, null);
+ // 获取标准的Java文件管理器实例
+ StandardJavaFileManager standardJavaFileManager = compiler.getStandardFileManager(diagnosticCollector, null, null);
+ // 初始化自定义Java文件管理器实例
+ JitJavaFileManager jitJavaFileManager = new JitJavaFileManager(standardJavaFileManager);
String classPath = StringUtils.join(refers, File.pathSeparator);
+
List options = new ArrayList<>();
- options.add("-d");
- options.add(destDir);
options.add("-classpath");
options.add(classPath);
- logger.info("compile outPath: {}", destDir);
logger.info("compile classPath: {}", classPath);
- boolean isSuccess = compiler.getTask(null, fileManager, diagnosticCollector, options, null, javaFiles).call();
+ boolean isSuccess = compiler.getTask(null, jitJavaFileManager, diagnosticCollector, options, null, javaFiles).call();
logger.info("compile end: {}", isSuccess);
try {
- fileManager.close();
- } catch (Exception e) {
- throw new RuntimeException(e.getMessage(), e);
+ jitJavaFileManager.close();
+ } catch (IOException e) {
+ throw new JitIOException(e.getMessage(), e);
}
if (!isSuccess) {
List> compileError = diagnosticCollector.getDiagnostics();
String errorInfo = getCompileErrorInfo(compileError);
logCompileErrorClassSourceCode(compileError);
- throw new RuntimeException("编译Java文件出错:" + errorInfo);
+ throw new JitCompileException("编译Java文件出错:" + errorInfo);
}
+ return jitJavaFileManager;
}
private String getCompileErrorInfo(List> compileError) {
@@ -417,7 +340,7 @@ public class CompileServiceImpl implements CompileService {
}
}
}
- logger.error("编译Java文件出错,涉及的类源码有:" + System.lineSeparator() + compileErrorRes.toString());
+ logger.error("编译Java文件出错,涉及的类源码有:" + System.lineSeparator() + compileErrorRes);
}
}
diff --git a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/JitCompileException.java b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/JitCompileException.java
new file mode 100644
index 0000000000000000000000000000000000000000..60e0b7ef7caf7a717d86af9bcb514cec27803112
--- /dev/null
+++ b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/JitCompileException.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.inspur.edp.jit.core.compile;
+
+import io.iec.edp.caf.commons.exception.CAFRuntimeException;
+import io.iec.edp.caf.commons.exception.ExceptionLevel;
+
+/**
+ * @author Kaixuan Shi
+ * @since 2023/8/30
+ */
+public class JitCompileException extends CAFRuntimeException {
+
+ private static final String SERVICE_UNIT_CODE = "pfcommon";
+ private static final String EXCEPTION_CODE = "GSP_JIT_COMPILE_0001";
+
+ public JitCompileException(String compileErrorMsg) {
+ super(SERVICE_UNIT_CODE, EXCEPTION_CODE, compileErrorMsg, null, ExceptionLevel.Error, false);
+ }
+}
diff --git a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/JitJavaFileManager.java b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/JitJavaFileManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..d96182091993894fffabbaae1f5001c923f596fc
--- /dev/null
+++ b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/JitJavaFileManager.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.inspur.edp.jit.core.compile;
+
+import javax.tools.FileObject;
+import javax.tools.ForwardingJavaFileManager;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author Kaixuan Shi
+ * @since 2023/7/7
+ */
+public class JitJavaFileManager extends ForwardingJavaFileManager {
+
+ //存放编译后的class
+ private final Map outputJavaFileObjectMap = new ConcurrentHashMap<>();
+
+ //存放编译后的类名及其对应的内部class的类名
+ private final List innerClassNames = new ArrayList<>();
+
+ //返回类及其内部类
+ public List getCompiledJavaFileObject(String className) {
+ List fileObjects = new ArrayList<>();
+ BytesJavaFileObject bytesJavaFileObject = outputJavaFileObjectMap.get(className);
+ if (bytesJavaFileObject != null) {
+ fileObjects.add(bytesJavaFileObject);
+ innerClassNames.forEach(innerClassName -> {
+ if (innerClassName.startsWith(className)) {
+ fileObjects.add(outputJavaFileObjectMap.get(innerClassName));
+ }
+ });
+ }
+ return fileObjects;
+ }
+
+ public JitJavaFileManager(JavaFileManager fileManager) {
+ super(fileManager);
+ }
+
+ /**
+ * 这里是编译器返回的同(源)Java文件对象,替换为StringJavaFileObject实现
+ */
+ @Override
+ public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) {
+ BytesJavaFileObject javaFileObject = new BytesJavaFileObject(className, kind);
+ outputJavaFileObjectMap.put(className, javaFileObject);
+ if (className.contains("$")) {
+ innerClassNames.add(className);
+ }
+ return javaFileObject;
+ }
+
+}
diff --git a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/Manifest.java b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/Manifest.java
index a9866dab6f4d4b69278fac62bd61aa530a129367..894e4a50cf0c51ae9eb1ea8046b6c83dd69f69dd 100644
--- a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/Manifest.java
+++ b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/Manifest.java
@@ -23,10 +23,18 @@ import java.util.Map;
@Data
public class Manifest {
+ /**
+ * 当前程序的JIT编译器版本
+ */
+ public static final String currentJitVersion = "2.0";
/**
* 版本
*/
private String version;
+ /**
+ * JIT编译器版本
+ */
+ private String jitVersion;
/**
* 编译时间
*/
diff --git a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/ManifestUtil.java b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/ManifestUtil.java
index 49bc96447d962fb510a2eed10dcaf21a0a60addf..1f7e5ca0a841d2febe0e203898efa57c2033dcbc 100644
--- a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/ManifestUtil.java
+++ b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/ManifestUtil.java
@@ -27,6 +27,7 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.inspur.edp.jit.api.compile.CompileContext;
import com.inspur.edp.jit.core.common.DirectoryManager;
+import com.inspur.edp.jit.core.util.JitIOException;
import io.iec.edp.caf.common.JSONSerializer;
import org.apache.commons.io.FileUtils;
@@ -47,6 +48,7 @@ public class ManifestUtil {
public static void updateManifest(CompileContext context) {
Manifest manifest = new Manifest();
manifest.setVersion(context.getVersion());
+ manifest.setJitVersion(Manifest.currentJitVersion);
manifest.setCompileTime(new Date());
manifest.setDescription(context.getDescription());
manifest.setExtendProperties(context.getExtendProperties());
@@ -55,16 +57,16 @@ public class ManifestUtil {
File file = getManifestJsonFile(context.getFunctionType(), context.getFunctionId());
FileUtils.write(file, content, "utf-8", false);
} catch (IOException e) {
- throw new RuntimeException(e.getMessage(), e);
+ throw new JitIOException(e.getMessage(), e);
}
}
/**
- * 得到功能包的Manifest信息,如果功能包不存在或者manifest.json文件不存在,则返回null。
+ * 得到功能包的Manifest信息,如果功能包不存在或者manifest.json文件不存在或者JIT版本不一致,则返回null。
*
* @param functionType 功能包类型
* @param functionId 功能包Id
- * @return 功能包的Manifest信息,如果功能包不存在或者manifest.json文件不存在,则返回null
+ * @return 功能包的Manifest信息,如果功能包不存在或者manifest.json文件不存在或者JIT版本不一致,则返回null
*/
public static Manifest getManifest(String functionType, String functionId) {
File file = getManifestJsonFile(functionType, functionId);
@@ -77,10 +79,11 @@ public class ManifestUtil {
if (content.isEmpty()) {
return new Manifest();
}
-
- return JSONSerializer.deserialize(content, Manifest.class);
+ //若jit版本不一致,则返回空manifest,通知外层该功能包缓存已不可用
+ Manifest manifest = JSONSerializer.deserialize(content, Manifest.class);
+ return Manifest.currentJitVersion.equals(manifest.getJitVersion()) ? manifest : null;
} catch (IOException e) {
- throw new RuntimeException(e.getMessage(), e);
+ throw new JitIOException(e.getMessage(), e);
}
}
diff --git a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/StringJavaFileObject.java b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/StringJavaFileObject.java
index 02fc698900c49289be2197d4848186f04dc3bfd4..04af1a5c214e422d1a6d7f9fac89eca8994a1b84 100644
--- a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/StringJavaFileObject.java
+++ b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/compile/StringJavaFileObject.java
@@ -15,9 +15,8 @@
*/
package com.inspur.edp.jit.core.compile;
-
-import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
+import javax.tools.JavaFileObject;
import java.net.URI;
public class StringJavaFileObject extends SimpleJavaFileObject {
diff --git a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/config/JitConfig.java b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/config/JitConfig.java
index b44fc1ee7acc7a885217ce4d1ea03730b28da9bc..c763b22f7fb6f19b4cbcea0a1699bbb0a2c34ed6 100644
--- a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/config/JitConfig.java
+++ b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/config/JitConfig.java
@@ -29,10 +29,9 @@ import org.springframework.context.annotation.Configuration;
/**
* 功能描述:
*
- * @ClassName: JitConfig
- * @Author: Fynn Qi
- * @Date: 2021/1/25 16:52
- * @Version: V1.0
+ * @author Fynn Qi
+ * @version 1.0
+ * @since 2021/1/25 16:52
*/
@Configuration(value = "com.inspur.edp.jit.core.config.JitConfig", proxyBeanMethods = false)
public class JitConfig {
diff --git a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/load/ClassLoadManager.java b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/load/ClassLoadManager.java
index 99e0ce59d88b3e02a14fe3e8276ccb31a159393a..2bba87dc6b8f513087dc9847f8c8f8b172c30c36 100644
--- a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/load/ClassLoadManager.java
+++ b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/load/ClassLoadManager.java
@@ -17,45 +17,56 @@
package com.inspur.edp.jit.core.load;
import com.inspur.edp.jit.core.common.DirectoryManager;
+import com.inspur.edp.jit.core.compile.BytesJavaFileObject;
+import com.inspur.edp.jit.core.util.JarUtil;
-import java.io.IOException;
-import java.util.HashMap;
+import java.io.FileNotFoundException;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
public class ClassLoadManager {
- private static Map> classLoaderMap = new ConcurrentHashMap<>();
+ private static final ConcurrentMap> classLoaderMap = new ConcurrentHashMap<>();
+
+ public static void put(String functionType, String functionId, List javaFileObjects) {
+ Map classBytesMap = javaFileObjects.stream().collect(Collectors
+ .toConcurrentMap(BytesJavaFileObject::getClassName, BytesJavaFileObject::getClassBytes));
+
+ ConcurrentMap functionTypeMap = classLoaderMap
+ .computeIfAbsent(functionType, key -> new ConcurrentHashMap<>());
+ functionTypeMap.put(functionId, new JitClassLoader(classBytesMap));
+ }
+
+ public static Class> load(String functionType, String functionId, String className) {
+ classLoaderMap.computeIfAbsent(functionType, key -> new ConcurrentHashMap<>());
+ JitClassLoader classLoader = classLoaderMap.get(functionType).computeIfAbsent(functionId, key -> {
+ Map classBytes = null;
+ try {
+ classBytes = getClassBytesFromCache(functionType, functionId);
+ } catch (FileNotFoundException e) {
+ throw new JitClassNotFoundException(functionType, functionId, className, e);
+ }
+ return new JitClassLoader(classBytes);
+ });
- public static Class load(String functionType, String functionId, String fullName) {
- if (!classLoaderMap.containsKey(functionType)) {
- classLoaderMap.put(functionType, new HashMap<>());
- }
- JitClassLoader classLoader = null;
- if (!classLoaderMap.get(functionType).containsKey(functionId)) {
- String jitDirectory = DirectoryManager.getJitDirectory(functionType, functionId);
- classLoader = new JitClassLoader(jitDirectory);
- classLoaderMap.get(functionType).put(functionId, classLoader);
- } else {
- classLoader = classLoaderMap.get(functionType).get(functionId);
- }
try {
- return classLoader.loadClass(fullName);
+ return classLoader.loadClass(className);
} catch (ClassNotFoundException e) {
- throw new RuntimeException("load class error ", e);
+ throw new JitClassNotFoundException(functionType, functionId, className, e);
}
}
+ private static Map getClassBytesFromCache(String functionType, String functionId) throws FileNotFoundException {
+ String jitDirectory = DirectoryManager.getJitDirectory(functionType, functionId);
+ return JarUtil.read(jitDirectory, functionId);
+ }
public static void unLoad(String functionType, String functionId) {
- if (classLoaderMap.containsKey(functionType) && classLoaderMap.get(functionType).containsKey(functionId)) {
- JitClassLoader loader = classLoaderMap.get(functionType).get(functionId);
+ if (classLoaderMap.containsKey(functionType)) {
classLoaderMap.get(functionType).remove(functionId);
- try {
- loader.close();
- } catch (IOException e) {
- throw new RuntimeException("close class loader failure:", e);
- }
}
}
diff --git a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/load/ClassLoadServiceImpl.java b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/load/ClassLoadServiceImpl.java
index 7b96854cbf589735865592aafd153e81b68c09c0..c42699a07a136e7ed1f96324b5678f96eac5949e 100644
--- a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/load/ClassLoadServiceImpl.java
+++ b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/load/ClassLoadServiceImpl.java
@@ -20,7 +20,7 @@ import com.inspur.edp.jit.api.load.ClassloaderService;
public class ClassLoadServiceImpl implements ClassloaderService {
@Override
- public Class load(String functionType, String functionId, String fullName) {
+ public Class> load(String functionType, String functionId, String fullName) {
return ClassLoadManager.load(functionType, functionId, fullName);
}
diff --git a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/load/JitClassLoader.java b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/load/JitClassLoader.java
index 93d82fced915f1f9ddbd2a55a637331513b7f869..98742b1c55836b79d1a317b3eb4c511d02430fe8 100644
--- a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/load/JitClassLoader.java
+++ b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/load/JitClassLoader.java
@@ -16,25 +16,43 @@
package com.inspur.edp.jit.core.load;
-import java.io.File;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
+import java.text.MessageFormat;
+import java.util.Map;
-public class JitClassLoader extends URLClassLoader {
+/**
+ * 加载StringJavaFileObject
+ */
+public class JitClassLoader extends ClassLoader {
+
+ private final Map classBytesMap;
+
+ //class字节码初始化JitClassLoader,入参不允许为null或空集合
+ public JitClassLoader(Map classBytes) {
+ super(ClassLoadManager.class.getClassLoader());
+ if (classBytes == null || classBytes.isEmpty()) {
+ throw new IllegalArgumentException("构造JitClassLoader的字节码集合不允许为空");
+ }
+ this.classBytesMap = classBytes;
+ }
- public JitClassLoader(String path) {
- super(getURLsByPath(path), ClassLoadManager.class.getClassLoader());
+ @Override
+ protected Class> findClass(String className) throws ClassNotFoundException {
+ if (!classBytesMap.containsKey(className)) {
+ throw new ClassNotFoundException(
+ MessageFormat.format("the class to load:{0},current classes:{1}", className, getClassNames())
+ );
+ }
+ byte[] classBytes = this.classBytesMap.get(className);
+ return defineClass(className, classBytes, 0, classBytes.length);
}
- private static URL[] getURLsByPath(String path) {
- File programRootDir = new File(path);
- URL[] urls = new URL[1];
- try {
- urls[0] = programRootDir.toURI().toURL();
- } catch (MalformedURLException e) {
- throw new RuntimeException(e.getMessage(), e);
+ private String getClassNames() {
+ String[] names = classBytesMap.keySet().toArray(new String[0]);
+ StringBuilder builder = new StringBuilder();
+ for (String name : names) {
+ builder.append(name).append(", ");
}
- return urls;
+ builder.delete(builder.length() - 2, builder.length());
+ return builder.toString();
}
}
diff --git a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/load/JitClassNotFoundException.java b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/load/JitClassNotFoundException.java
new file mode 100644
index 0000000000000000000000000000000000000000..6c7d7bcaa7f23d428fb6c56495bea90c4049b154
--- /dev/null
+++ b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/load/JitClassNotFoundException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.inspur.edp.jit.core.load;
+
+import io.iec.edp.caf.commons.exception.CAFRuntimeException;
+import io.iec.edp.caf.commons.exception.ExceptionLevel;
+
+import java.text.MessageFormat;
+
+/**
+ * @author Kaixuan Shi
+ * @since 2023/7/11
+ */
+public class JitClassNotFoundException extends CAFRuntimeException {
+
+ private static final String SERVICE_UNIT_CODE = "pfcommon";
+ private static final String EXCEPTION_CODE = "GSP_JIT_LOAD_0001";
+
+ public JitClassNotFoundException(String functionType, String functionId, String className, Exception e) {
+ super(SERVICE_UNIT_CODE, EXCEPTION_CODE, MessageFormat.format("functionType:{0}, functionId:{1}, className:{2}",
+ functionType, functionId, className), e, ExceptionLevel.Error, false);
+ }
+
+ public JitClassNotFoundException(String functionType, String functionId, String className) {
+ this(functionType, functionId, className, null);
+ }
+
+}
diff --git a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/query/QueryServiceImpl.java b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/query/QueryServiceImpl.java
index 98cf1fa0ba0ccccc58ac7445e60257d55d4ce360..0007787d5da687b4ebecbf0dcf76fa5d0a7068eb 100644
--- a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/query/QueryServiceImpl.java
+++ b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/query/QueryServiceImpl.java
@@ -21,6 +21,7 @@ import com.inspur.edp.jit.api.query.QueryService;
import com.inspur.edp.jit.core.common.DirectoryManager;
import com.inspur.edp.jit.core.compile.Manifest;
import com.inspur.edp.jit.core.compile.ManifestUtil;
+import com.inspur.edp.jit.core.util.JitIOException;
import org.apache.commons.io.FileUtils;
import java.io.File;
@@ -80,7 +81,7 @@ public class QueryServiceImpl implements QueryService {
FileUtils.forceDelete(dir);
}
} catch (IOException e) {
- throw new RuntimeException(e.getMessage(), e);
+ throw new JitIOException(e.getMessage(), e);
}
}
@@ -93,7 +94,7 @@ public class QueryServiceImpl implements QueryService {
FileUtils.forceDelete(dir);
}
} catch (IOException e) {
- throw new RuntimeException(e.getMessage(), e);
+ throw new JitIOException(e.getMessage(), e);
}
}
diff --git a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/util/JarUtil.java b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/util/JarUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8b4a82db37f16b173d395426947815ab45ed9f2
--- /dev/null
+++ b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/util/JarUtil.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.inspur.edp.jit.core.util;
+
+import com.inspur.edp.jit.core.common.DirectoryManager;
+import com.inspur.edp.jit.core.compile.BytesJavaFileObject;
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
+
+/**
+ * @author Kaixuan Shi
+ * @since 2023/8/24
+ */
+public class JarUtil {
+
+ //JAR文件后缀,避免采用.jar被轻易识别
+ private final static String JAR_FILE_EXTENSION = ".jit";
+ //JAR文件中目录分隔符,正斜杠(/),不区分操作系统
+ private final static char JAR_FILE_SEPARATOR = '/';
+ private final static String CLASS_FILE_EXTENSION = ".class";
+
+ private final static Logger logger = LoggerFactory.getLogger(JarUtil.class);
+
+ /**
+ * 保存class字节码至目录的jar类型文件中
+ *
+ * @param dir 文件目录
+ * @param fileName 文件名,不带后缀
+ * @param compiledClasses 编译后的class字节码
+ */
+ public static void write(String dir, String fileName, List compiledClasses) {
+ String jarFileName = getJARFileName(dir, fileName);
+ try (JarOutputStream jarOutputStream = new JarOutputStream(Files.newOutputStream(Paths.get(jarFileName)))) {
+ for (BytesJavaFileObject javaFileObject : compiledClasses) {
+ // 写入类的字节码到JAR文件
+ jarOutputStream.putNextEntry(new JarEntry(getJARFileEntryName(javaFileObject.getClassName())));
+ jarOutputStream.write(javaFileObject.getClassBytes());
+ jarOutputStream.closeEntry();
+ }
+ } catch (IOException e) {
+ throw new JitIOException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * 从JAR中读取类字节码
+ *
+ * @param dir 文件目录
+ * @param fileName 文件名,不带后缀
+ * @return 类名及对应的字节码。文件不存在则返回null
+ * @throws FileNotFoundException 文件不存在
+ */
+ public static Map read(String dir, String fileName) throws FileNotFoundException {
+ String jarFileName = getJARFileName(dir, fileName);
+ File jarFile = new File(jarFileName);
+ if (!jarFile.exists()) {
+ throw new FileNotFoundException(jarFileName);
+ }
+ try (JarInputStream jarInputStream = new JarInputStream(Files.newInputStream(Paths.get(jarFileName)))) {
+ Map classMap = new HashMap<>();
+ JarEntry jarEntry;
+ while ((jarEntry = jarInputStream.getNextJarEntry()) != null) {
+ if (!jarEntry.isDirectory() && jarEntry.getName().endsWith(CLASS_FILE_EXTENSION)) {
+ String className = jarEntry.getName()
+ .substring(0, jarEntry.getName().length() - CLASS_FILE_EXTENSION.length())
+ .replace(JAR_FILE_SEPARATOR, '.');
+ byte[] classBytes = IOUtils.toByteArray(jarInputStream);
+ classMap.put(className, classBytes);
+ }
+ }
+ return classMap;
+ } catch (IOException e) {
+ throw new JitIOException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * 根据全限定类名获取其在JAR文件中的路径
+ */
+ private static String getJARFileEntryName(String className) {
+ return className.replace('.', JAR_FILE_SEPARATOR) + CLASS_FILE_EXTENSION;
+ }
+
+ /**
+ * @param dir 文件目录
+ * @param fileName 文件名,不带后缀
+ * @return 完整文件路径,文件名带后缀
+ */
+ private static String getJARFileName(String dir, String fileName) {
+ return DirectoryManager.joinFilePath(dir, fileName + JAR_FILE_EXTENSION);
+ }
+}
diff --git a/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/util/JitIOException.java b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/util/JitIOException.java
new file mode 100644
index 0000000000000000000000000000000000000000..9ac81493a73e2e6a737c5fa7c77c0ff2541612b3
--- /dev/null
+++ b/rest-api-jit-core/src/main/java/com/inspur/edp/jit/core/util/JitIOException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.inspur.edp.jit.core.util;
+
+import io.iec.edp.caf.commons.exception.CAFRuntimeException;
+import io.iec.edp.caf.commons.exception.ExceptionLevel;
+
+import java.io.IOException;
+
+/**
+ * @author Kaixuan Shi
+ * @since 2023/8/24
+ */
+public class JitIOException extends CAFRuntimeException {
+
+ private static final String SERVICE_UNIT_CODE = "pfcommon";
+ private static final String EXCEPTION_CODE = "GSP_JIT_IO_0001";
+
+ public JitIOException(String path, IOException ioException) {
+ super(SERVICE_UNIT_CODE, EXCEPTION_CODE, path, ioException, ExceptionLevel.Error, false);
+ }
+
+}
diff --git a/rest-api-jit-core/src/test/java/com/inspur/edp/jit/core/load/ClassLoadServiceTest.java b/rest-api-jit-core/src/test/java/com/inspur/edp/jit/core/load/ClassLoadServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d77e4ce4d3b75d24e97858ff54eb05f601031aac
--- /dev/null
+++ b/rest-api-jit-core/src/test/java/com/inspur/edp/jit/core/load/ClassLoadServiceTest.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.inspur.edp.jit.core.load;
+
+import com.inspur.edp.jit.api.compile.CompileContext;
+import com.inspur.edp.jit.api.compile.CompileService;
+import com.inspur.edp.jit.api.compile.JavaSourceCode;
+import com.inspur.edp.jit.api.load.ClassloaderService;
+import com.inspur.edp.jit.api.query.FunctionSummaryInfo;
+import com.inspur.edp.jit.api.query.QueryService;
+import com.inspur.edp.jit.core.common.DirectoryManager;
+import com.inspur.edp.jit.core.compile.CompileServiceImpl;
+import com.inspur.edp.jit.core.query.QueryServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.commons.lang3.reflect.MethodUtils;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.RepeatedTest;
+import org.junit.jupiter.api.Test;
+import org.springframework.core.io.ClassPathResource;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
+
+/**
+ * @author Kaixuan Shi
+ * @since 2023/8/21
+ */
+@Slf4j
+public class ClassLoadServiceTest {
+
+ private final CompileService compileService = new CompileServiceImpl();
+ private final ClassloaderService classloaderService = new ClassLoadServiceImpl();
+ private final QueryService queryService = new QueryServiceImpl();
+ private final static String FUNCTION_TYPE = "eapi";
+ private final static String HISTORY_FUNCTION_TYPE = "oldJit";
+ private final static String HISTORY_FUNCTION_ID = "5f88f4a7-ee63-4e00-8cd3-8c5f90c85355";
+
+ @BeforeAll
+ public static void prepare() throws IllegalAccessException {
+ log.info("执行测试前初始化");
+ //将根目录设置为当前resources目录
+ FieldUtils.writeDeclaredStaticField(DirectoryManager.class, "HOME_DIR", Paths.get(System.getProperty("user.dir"), "src", "test", "resources").toString(), true);
+ //将启动中事件创建的目录创建一下,见:com.inspur.edp.jit.core.common.StartManager
+ String jitBaseDirectory = DirectoryManager.getJitBaseDirectory();
+ DirectoryManager.createDir(jitBaseDirectory);
+ }
+
+ /**
+ * 测试QueryService的query及历史jit版本生成的cache能否正确查询并返回指定结果
+ */
+ @Test
+ public void testOldJitVersionFunctionCache() {
+ List functionSummaryInfos = queryService.query(HISTORY_FUNCTION_TYPE);
+ Assertions.assertEquals(functionSummaryInfos.size(), 1);
+ FunctionSummaryInfo functionSummaryInfo = queryService.query(HISTORY_FUNCTION_TYPE, HISTORY_FUNCTION_ID);
+ functionSummaryInfos.add(functionSummaryInfo);
+ for (FunctionSummaryInfo summaryInfo : functionSummaryInfos) {
+ Assertions.assertEquals(summaryInfo.getFunctionType(), HISTORY_FUNCTION_TYPE);
+ Assertions.assertEquals(summaryInfo.getFunctionId(), HISTORY_FUNCTION_ID);
+ Assertions.assertNull(summaryInfo.getDescription());
+ Assertions.assertNull(summaryInfo.getCompileTime());
+ Assertions.assertNull(summaryInfo.getVersion());
+ }
+ }
+
+ /**
+ * 测试CompileService#compile(CompileContext)的编译并加载类
+ */
+ @RepeatedTest(2)
+ public void testSingleContextCompile() throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+ //编译类
+ JavaSourceCode javaSourceCode = new JavaSourceCode();
+ javaSourceCode.setFullClassName("com.inspur.demo.Hello");
+ javaSourceCode.setSourceCode(getSourceCode("Hello.java"));
+ JavaSourceCode interfaceSource = new JavaSourceCode();
+ interfaceSource.setFullClassName("com.inspur.gs.scm.sd.salesorder.rest.ZUserVoExtend1Service");
+ interfaceSource.setSourceCode(getSourceCode("ZUserVoExtend1Service.java"));
+ JavaSourceCode implSource = new JavaSourceCode();
+ implSource.setFullClassName("com.inspur.gs.scm.sd.salesorder.rest.ZUserVoExtend1ServiceImpl");
+ implSource.setSourceCode(getSourceCode("ZUserVoExtend1ServiceImpl.java"));
+
+ List sourceCodes = new ArrayList<>();
+ sourceCodes.add(javaSourceCode);
+ sourceCodes.add(interfaceSource);
+ sourceCodes.add(implSource);
+
+ CompileContext compileContext = new CompileContext();
+ compileContext.setFunctionType(FUNCTION_TYPE);
+ compileContext.setFunctionId(UUID.randomUUID().toString());
+ compileContext.setVersion(compileContext.getFunctionId() + "#1.0.2");
+ compileContext.setDescription("测试生成");
+ compileContext.setSourceCodes(sourceCodes);
+ compileContext.setRefers(getRefers());
+ compileContext.setReferBoots(getReferBoots());
+ Map extendProperties = new HashMap<>();
+ extendProperties.put(FUNCTION_TYPE, HISTORY_FUNCTION_ID);
+ compileContext.setExtendProperties(extendProperties);
+
+ compileService.compile(compileContext);
+
+ //1、加载类
+ {
+ Class> clazz0 = classloaderService.load(compileContext.getFunctionType(), compileContext.getFunctionId(),
+ compileContext.getSourceCodes().get(0).getFullClassName());
+ Assertions.assertEquals(
+ MethodUtils.invokeMethod(clazz0.newInstance(), "createSession"), "createSession");
+ Class> clazz2 = classloaderService.load(compileContext.getFunctionType(), compileContext.getFunctionId(),
+ compileContext.getSourceCodes().get(2).getFullClassName());
+ Assertions.assertEquals(
+ MethodUtils.invokeMethod(clazz2.newInstance(), "createSession"), "createSession");
+
+ //加载内部类
+ Class> innerClazz = classloaderService.load(compileContext.getFunctionType(), compileContext.getFunctionId(),
+ compileContext.getSourceCodes().get(0).getFullClassName() + "$InnerClass");
+ Assertions.assertEquals(
+ MethodUtils.invokeMethod(
+ innerClazz.getDeclaredConstructors()[0]
+ .newInstance(clazz0.newInstance()), "createSession"
+ ), "createSession"
+ );
+ //加载不存在的类
+ Assertions.assertThrows(JitClassNotFoundException.class, () ->
+ classloaderService.load("notExist", "no", "this.is.a.not.exist.class")
+ );
+ }
+
+ //测试类卸载接口
+ testUnLoadService(compileContext);
+
+ //测试从功能包缓存中加载类
+ FunctionSummaryInfo functionSummaryInfo = queryService.query(compileContext.getFunctionType(), compileContext.getFunctionId());
+ Assertions.assertEquals(functionSummaryInfo.getVersion(), compileContext.getVersion());
+ Assertions.assertEquals(functionSummaryInfo.getExtendProperties().get(FUNCTION_TYPE), HISTORY_FUNCTION_ID);
+ //2、加载类,从1中复制
+ {
+ Class> clazz0 = classloaderService.load(compileContext.getFunctionType(), compileContext.getFunctionId(),
+ compileContext.getSourceCodes().get(0).getFullClassName());
+ Assertions.assertEquals(
+ MethodUtils.invokeMethod(clazz0.newInstance(), "createSession"), "createSession");
+ Class> clazz2 = classloaderService.load(compileContext.getFunctionType(), compileContext.getFunctionId(),
+ compileContext.getSourceCodes().get(2).getFullClassName());
+ Assertions.assertEquals(
+ MethodUtils.invokeMethod(clazz2.newInstance(), "createSession"), "createSession");
+
+ //加载内部类
+ Class> innerClazz = classloaderService.load(compileContext.getFunctionType(), compileContext.getFunctionId(),
+ compileContext.getSourceCodes().get(0).getFullClassName() + "$InnerClass");
+ Assertions.assertEquals(
+ MethodUtils.invokeMethod(
+ innerClazz.getDeclaredConstructors()[0]
+ .newInstance(clazz0.newInstance()), "createSession"
+ ), "createSession"
+ );
+ //加载不存在的类
+ Assertions.assertThrows(JitClassNotFoundException.class, () ->
+ classloaderService.load("notExist", "no", "this.is.a.not.exist.class")
+ );
+ }
+
+ }
+
+ /**
+ * 测试CompileServiceImpl#batchCompile(List)的批量编译并加载类
+ */
+ @RepeatedTest(2)
+ public void testMultiContextsBatchCompile() throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+ //编译类
+ JavaSourceCode javaSourceCode = new JavaSourceCode();
+ javaSourceCode.setFullClassName("com.inspur.demo.Hello");
+ javaSourceCode.setSourceCode(getSourceCode("Hello.java"));
+
+ List sourceCodes1 = new ArrayList<>();
+ sourceCodes1.add(javaSourceCode);
+
+ JavaSourceCode interfaceSource = new JavaSourceCode();
+ interfaceSource.setFullClassName("com.inspur.gs.scm.sd.salesorder.rest.ZUserVoExtend1Service");
+ interfaceSource.setSourceCode(getSourceCode("ZUserVoExtend1Service.java"));
+ JavaSourceCode implSource = new JavaSourceCode();
+ implSource.setFullClassName("com.inspur.gs.scm.sd.salesorder.rest.ZUserVoExtend1ServiceImpl");
+ implSource.setSourceCode(getSourceCode("ZUserVoExtend1ServiceImpl.java"));
+
+ List sourceCodes2 = new ArrayList<>();
+ sourceCodes2.add(interfaceSource);
+ sourceCodes2.add(implSource);
+
+ List referBoots = getReferBoots();
+ List refers = getRefers();
+
+ CompileContext compileContext1 = new CompileContext();
+ compileContext1.setFunctionType(FUNCTION_TYPE);
+ compileContext1.setFunctionId("context1");
+ compileContext1.setVersion(compileContext1.getFunctionId() + "#1.0.2");
+ compileContext1.setDescription("测试生成1");
+ compileContext1.setSourceCodes(sourceCodes1);
+ compileContext1.setRefers(refers);
+ compileContext1.setReferBoots(referBoots);
+
+ CompileContext compileContext2 = new CompileContext();
+ compileContext2.setFunctionType(FUNCTION_TYPE);
+ compileContext2.setFunctionId("context2");
+ compileContext2.setVersion(compileContext2.getFunctionId() + "#1.0.2");
+ compileContext2.setDescription("测试生成2");
+ compileContext2.setSourceCodes(sourceCodes2);
+ compileContext2.setRefers(refers);
+ compileContext2.setReferBoots(referBoots);
+ //测试不同Context下相同类名的隔离性
+ CompileContext compileContext3 = new CompileContext();
+ compileContext3.setFunctionType(FUNCTION_TYPE);
+ compileContext3.setFunctionId("context3");
+ compileContext3.setVersion(compileContext3.getFunctionId() + "#1.0.2");
+ compileContext3.setDescription("测试生成3");
+ compileContext3.setSourceCodes(sourceCodes2);
+ compileContext3.setRefers(refers);
+ compileContext3.setReferBoots(referBoots);
+
+ List contexts = new ArrayList<>();
+ contexts.add(compileContext1);
+ contexts.add(compileContext2);
+ contexts.add(compileContext3);
+
+ compileService.batchCompile(contexts);
+
+ //1、加载类
+ {
+ Class> clazz1 = classloaderService.load(compileContext1.getFunctionType(), compileContext1.getFunctionId(),
+ compileContext1.getSourceCodes().get(0).getFullClassName());
+ Assertions.assertEquals(
+ MethodUtils.invokeMethod(clazz1.newInstance(), "createSession"), "createSession"
+ );
+ Class> clazz2 = classloaderService.load(compileContext2.getFunctionType(), compileContext2.getFunctionId(),
+ compileContext2.getSourceCodes().get(1).getFullClassName());
+ Assertions.assertEquals(
+ MethodUtils.invokeMethod(clazz2.newInstance(), "createSession"), "createSession"
+ );
+ Class> clazz3 = classloaderService.load(compileContext3.getFunctionType(), compileContext3.getFunctionId(),
+ compileContext3.getSourceCodes().get(1).getFullClassName());
+ Assertions.assertEquals(
+ MethodUtils.invokeMethod(clazz3.newInstance(), "createSession"), "createSession"
+ );
+
+ //加载内部类
+ Class> innerClazz = classloaderService.load(compileContext1.getFunctionType(), compileContext1.getFunctionId(),
+ compileContext1.getSourceCodes().get(0).getFullClassName() + "$InnerClass");
+ Assertions.assertEquals(
+ MethodUtils.invokeMethod(
+ innerClazz.getDeclaredConstructors()[0]
+ .newInstance(clazz1.newInstance()), "createSession"
+ ), "createSession"
+ );
+ //测试不同FunctionId的类隔离性
+ Assertions.assertThrows(JitClassNotFoundException.class,
+ () -> classloaderService.load(compileContext2.getFunctionType(), compileContext2.getFunctionId(),
+ compileContext1.getSourceCodes().get(0).getFullClassName()));
+ }
+ //测试卸载类接口
+ for (CompileContext context : contexts) {
+ testUnLoadService(context);
+ }
+ //测试从功能包中加载类
+ List summaryInfos = queryService.query(FUNCTION_TYPE);
+ //过滤出该测试类中的contexts对应的功能包
+ for (CompileContext context : contexts) {
+ FunctionSummaryInfo contextSummaryInfo = summaryInfos.stream().filter(summaryInfo ->
+ summaryInfo.getFunctionId().equals(context.getFunctionId())).collect(Collectors.toList())
+ .get(0);
+ Assertions.assertEquals(contextSummaryInfo.getVersion(), context.getVersion());
+ }
+
+ //2、加载类
+ {
+ Class> clazz1 = classloaderService.load(compileContext1.getFunctionType(), compileContext1.getFunctionId(),
+ compileContext1.getSourceCodes().get(0).getFullClassName());
+ Assertions.assertEquals(
+ MethodUtils.invokeMethod(clazz1.newInstance(), "createSession"), "createSession"
+ );
+ Class> clazz2 = classloaderService.load(compileContext2.getFunctionType(), compileContext2.getFunctionId(),
+ compileContext2.getSourceCodes().get(1).getFullClassName());
+ Assertions.assertEquals(
+ MethodUtils.invokeMethod(clazz2.newInstance(), "createSession"), "createSession"
+ );
+ Class> clazz3 = classloaderService.load(compileContext3.getFunctionType(), compileContext3.getFunctionId(),
+ compileContext3.getSourceCodes().get(1).getFullClassName());
+ Assertions.assertEquals(
+ MethodUtils.invokeMethod(clazz3.newInstance(), "createSession"), "createSession"
+ );
+
+ //加载内部类
+ Class> innerClazz = classloaderService.load(compileContext1.getFunctionType(), compileContext1.getFunctionId(),
+ compileContext1.getSourceCodes().get(0).getFullClassName() + "$InnerClass");
+ Assertions.assertEquals(
+ MethodUtils.invokeMethod(
+ innerClazz.getDeclaredConstructors()[0]
+ .newInstance(clazz1.newInstance()), "createSession"
+ ), "createSession"
+ );
+ //测试不同FunctionId的类隔离性
+ Assertions.assertThrows(JitClassNotFoundException.class,
+ () -> classloaderService.load(compileContext2.getFunctionType(), compileContext2.getFunctionId(),
+ compileContext1.getSourceCodes().get(0).getFullClassName()));
+ }
+
+ }
+
+ @AfterAll
+ public static void deleteCreatedFiles() throws IOException {
+ String generatedFunctionDir = DirectoryManager.getJitDirectory(FUNCTION_TYPE);
+ log.info("清理测试生成文件夹:" + generatedFunctionDir);
+ if (new File(generatedFunctionDir).exists()) {
+ FileUtils.forceDelete(new File(generatedFunctionDir));
+ }
+ }
+
+ /**
+ * 测试类卸载接口
+ */
+ @SuppressWarnings("unchecked")
+ private void testUnLoadService(CompileContext compileContext) throws IllegalAccessException {
+ Map> classLoadMap =
+ (ConcurrentMap>) FieldUtils
+ .readStaticField(ClassLoadManager.class, "classLoaderMap", true);
+ Assertions.assertTrue(classLoadMap.get(compileContext.getFunctionType()).containsKey(compileContext.getFunctionId()));
+ classloaderService.unload(compileContext.getFunctionType(), compileContext.getFunctionId());
+ Assertions.assertFalse(classLoadMap.get(compileContext.getFunctionType()).containsKey(compileContext.getFunctionId()));
+ }
+
+ private List getReferBoots() {
+ List referBoots = new ArrayList<>();
+ referBoots.add("jackson-databind");
+ referBoots.add("jackson-core");
+ referBoots.add("jakarta.ws.rs-api");
+ referBoots.add("lombok");
+ return referBoots;
+ }
+
+ private List getRefers() {
+ List refers = new ArrayList<>();
+ refers.add("refers" + File.separator + "cdp-sgf-base.jar");
+ refers.add("refers" + File.separator + "cdp-sgf-api.jar");
+ refers.add("refers" + File.separator + "jit-api-1.0.2.jar");
+ return refers;
+ }
+
+ public static String getSourceCode(String fileName) {
+ String resourceLocation = "source_code" + File.separator + fileName;
+ ClassPathResource classPathResource = new ClassPathResource(resourceLocation);
+ try {
+ InputStream inputStream = classPathResource.getInputStream();
+ String code = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
+ inputStream.close();
+ return code;
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+}
diff --git a/rest-api-jit-core/src/test/java/com/inspur/edp/jit/core/load/ExceptionTest.java b/rest-api-jit-core/src/test/java/com/inspur/edp/jit/core/load/ExceptionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..02eb75971061d7393c7041f39b16d5be6cee3016
--- /dev/null
+++ b/rest-api-jit-core/src/test/java/com/inspur/edp/jit/core/load/ExceptionTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.inspur.edp.jit.core.load;
+
+import com.inspur.edp.jit.api.compile.CompileContext;
+import com.inspur.edp.jit.api.compile.CompileService;
+import com.inspur.edp.jit.api.compile.JavaSourceCode;
+import com.inspur.edp.jit.core.compile.CompileServiceImpl;
+import com.inspur.edp.jit.core.util.JitIOException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * @author Kaixuan Shi
+ * @since 2023/8/30
+ */
+public class ExceptionTest {
+ private final CompileService compileService = new CompileServiceImpl();
+ private final static String FUNCTION_TYPE = "eapi";
+
+
+ @Test
+ public void testCompileException() {
+ //编译类
+ JavaSourceCode javaSourceCode = new JavaSourceCode();
+ javaSourceCode.setFullClassName("com.inspur.demo.Hello");
+ javaSourceCode.setSourceCode(ClassLoadServiceTest.getSourceCode("Hello.java"));
+
+ List sourceCodes = new ArrayList<>();
+ sourceCodes.add(javaSourceCode);
+
+ CompileContext compileContext = new CompileContext();
+ compileContext.setFunctionType(FUNCTION_TYPE);
+ compileContext.setFunctionId(UUID.randomUUID().toString());
+ compileContext.setVersion(compileContext.getFunctionId() + "#1.0.2");
+ compileContext.setDescription("测试生成");
+ compileContext.setSourceCodes(sourceCodes);
+
+ Assertions.assertThrows(RuntimeException.class, () -> compileService.compile(compileContext));
+ }
+
+ @Test
+ public void testIOException() {
+ String errorMessage = "path";
+ try {
+ throw new JitIOException(errorMessage, new IOException("innerException"));
+
+ } catch (Exception e) {
+ Assertions.assertTrue(e instanceof JitIOException);
+ Assertions.assertEquals(errorMessage, e.getMessage());
+ Assertions.assertTrue(e.getCause() instanceof IOException);
+ }
+ }
+}
diff --git a/rest-api-jit-core/src/test/resources/refers/cdp-sgf-api.jar b/rest-api-jit-core/src/test/resources/refers/cdp-sgf-api.jar
new file mode 100644
index 0000000000000000000000000000000000000000..168e50aee60f8002119e618627aa9798494e4f44
Binary files /dev/null and b/rest-api-jit-core/src/test/resources/refers/cdp-sgf-api.jar differ
diff --git a/rest-api-jit-core/src/test/resources/refers/cdp-sgf-base.jar b/rest-api-jit-core/src/test/resources/refers/cdp-sgf-base.jar
new file mode 100644
index 0000000000000000000000000000000000000000..7dbe194b5c40ce85d7755e7bc377212c2280575a
Binary files /dev/null and b/rest-api-jit-core/src/test/resources/refers/cdp-sgf-base.jar differ
diff --git a/rest-api-jit-core/src/test/resources/refers/jit-api-1.0.2.jar b/rest-api-jit-core/src/test/resources/refers/jit-api-1.0.2.jar
new file mode 100644
index 0000000000000000000000000000000000000000..c0b7cc4ecfa93c510bfcfc8c355496f4d3a46d28
Binary files /dev/null and b/rest-api-jit-core/src/test/resources/refers/jit-api-1.0.2.jar differ
diff --git a/rest-api-jit-core/src/test/resources/runtime/3rd/jackson-core-2.14.1.jar b/rest-api-jit-core/src/test/resources/runtime/3rd/jackson-core-2.14.1.jar
new file mode 100644
index 0000000000000000000000000000000000000000..cc025836227e860986d604a1d763d981fc465787
Binary files /dev/null and b/rest-api-jit-core/src/test/resources/runtime/3rd/jackson-core-2.14.1.jar differ
diff --git a/rest-api-jit-core/src/test/resources/runtime/3rd/jackson-databind-2.14.1.jar b/rest-api-jit-core/src/test/resources/runtime/3rd/jackson-databind-2.14.1.jar
new file mode 100644
index 0000000000000000000000000000000000000000..1ac8096422b80872cf7b5f7de413ae985f5e5c6e
Binary files /dev/null and b/rest-api-jit-core/src/test/resources/runtime/3rd/jackson-databind-2.14.1.jar differ
diff --git a/rest-api-jit-core/src/test/resources/runtime/3rd/jakarta.ws.rs-api-2.1.6.jar b/rest-api-jit-core/src/test/resources/runtime/3rd/jakarta.ws.rs-api-2.1.6.jar
new file mode 100644
index 0000000000000000000000000000000000000000..4850659bb6c9f2872836f4d0d5f4600da774d440
Binary files /dev/null and b/rest-api-jit-core/src/test/resources/runtime/3rd/jakarta.ws.rs-api-2.1.6.jar differ
diff --git a/rest-api-jit-core/src/test/resources/runtime/3rd/lombok-1.18.16.jar b/rest-api-jit-core/src/test/resources/runtime/3rd/lombok-1.18.16.jar
new file mode 100644
index 0000000000000000000000000000000000000000..20f85ff2e00325f61d3987233be813794c30986b
Binary files /dev/null and b/rest-api-jit-core/src/test/resources/runtime/3rd/lombok-1.18.16.jar differ
diff --git a/rest-api-jit-core/src/test/resources/source_code/ErrorClass.java b/rest-api-jit-core/src/test/resources/source_code/ErrorClass.java
new file mode 100644
index 0000000000000000000000000000000000000000..aebede3c0a4efa26c9cd5ec8e039ef4d54407c03
--- /dev/null
+++ b/rest-api-jit-core/src/test/resources/source_code/ErrorClass.java
@@ -0,0 +1,11 @@
+package com.inspur.demo;
+
+/**
+ * 该类存在语法错误,用于测试编译异常
+ */
+public class ErrorClass {
+ public createSession() {
+ return "createSession";
+ }
+
+}
\ No newline at end of file
diff --git a/rest-api-jit-core/src/test/resources/source_code/Hello.java b/rest-api-jit-core/src/test/resources/source_code/Hello.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b2f391bdd09e952861a8dd32071709bca79007d
--- /dev/null
+++ b/rest-api-jit-core/src/test/resources/source_code/Hello.java
@@ -0,0 +1,18 @@
+package com.inspur.demo;
+
+import com.inspur.edp.jit.api.compile.CompileContext;
+
+/**
+ * 此类继承测试运行时环境中已有的一个类,判断继承的父类是否能被正确找到
+ */
+public class Hello extends CompileContext {
+ public String createSession() {
+ return "createSession";
+ }
+
+ public class InnerClass {
+ public String createSession() {
+ return "createSession";
+ }
+ }
+}
\ No newline at end of file
diff --git a/rest-api-jit-core/src/test/resources/source_code/ZUserVoExtend1Service.java b/rest-api-jit-core/src/test/resources/source_code/ZUserVoExtend1Service.java
new file mode 100644
index 0000000000000000000000000000000000000000..b099de62c96ff4ef0fe9264ee662f7a83d8f40db
--- /dev/null
+++ b/rest-api-jit-core/src/test/resources/source_code/ZUserVoExtend1Service.java
@@ -0,0 +1,112 @@
+package com.inspur.gs.scm.sd.salesorder.rest;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.inspur.edp.sgf.api.annotation.EapiService;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+
+@EapiService(id = "8ac64c96-5c03-45ff-bd0b-0fa2aefa9f90", code = "ZUserVoExtend1")
+@Path("/")
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public interface ZUserVoExtend1Service {
+
+ @POST
+ @Path("/service/createsession")
+ @Produces(MediaType.TEXT_PLAIN)
+ public String createSession();
+
+ @POST
+ @Path("/")
+ public Object create(JsonNode node);
+
+ @PUT
+ @Path("/service/edit/{dataId}")
+ public Object edit(@PathParam("dataId") String dataId, JsonNode node);
+
+ @DELETE
+ @Path("/{dataId}")
+ public void delete();
+
+ @DELETE
+ @Path("/")
+ public void batchDelete(@QueryParam("ids") String ids);
+
+ @PATCH
+ @Path("/")
+ public Object update(JsonNode node);
+
+ @GET
+ @Path("/{dataId}")
+ public Object retrieve(@PathParam("dataId") String dataId);
+
+ @PUT
+ @Path("/service/retrieve/{dataId}")
+ public Object retrieveWithChildPagination(@PathParam("dataId") String dataId, JsonNode node);
+
+ @PUT
+ @Path("/service/querychild")
+ public Object queryChild(JsonNode node);
+
+ @GET
+ @Path("/")
+ public Object query(@QueryParam("entityFilter") String entityFilter);
+
+ @PUT
+ @Path("/")
+ public Object save(JsonNode node);
+
+ @POST
+ @Path("/service/cancel")
+ public void cancel();
+
+ @GET
+ @Path("/elementhelps/{labelId}")
+ public Object getElementHelp(@PathParam("labelId") String labelId, @QueryParam("nodeCode") String nodeCode, @QueryParam("queryParam") String queryParam);
+
+ @POST
+ @Path("/{rootId}/userdetail")
+ public Object createChildUserDetail(@PathParam("rootId") String rootId, JsonNode node);
+
+ @DELETE
+ @Path("/{rootId}/userdetail/{userDetail}Id")
+ public void deleteChildUserDetail(@PathParam("rootId") String rootId, @PathParam("userDetailId") String userDetailId);
+
+ @PUT
+ @Path("/extension/delete/{dataId}")
+ public Object extend_Delete(JsonNode node);
+
+ @PUT
+ @Path("/extension/batchdelete")
+ public Object extend_BatchDelete(@QueryParam("ids") String ids, JsonNode node);
+
+ @PUT
+ @Path("/extension/retrieve/{dataId}")
+ public Object extend_Retrieve(@PathParam("dataId") String dataId, JsonNode node);
+
+ @PUT
+ @Path("/extension/query")
+ public Object extend_Query(@QueryParam("entityFilter") String entityFilter, JsonNode node);
+
+ @PUT
+ @Path("/extension/elementhelps")
+ public Object extend_GetElementHelp(JsonNode node);
+
+ @PUT
+ @Path("/extension/{rootId}/userdetail/{userDetailId}")
+ public Object extend_DeleteChildUserDetail(@PathParam("rootId") String rootId, @PathParam("userDetailId") String userDetailId, JsonNode node);
+
+ @PUT
+ @Path("/service/datadeser")
+ public Object dataDeser(JsonNode node);
+
+ @PUT
+ @Path("/service/submit")
+ public Object submit(JsonNode node);
+
+ @PUT
+ @Path("/service/t1")
+ public Object t1(JsonNode node);
+
+}
\ No newline at end of file
diff --git a/rest-api-jit-core/src/test/resources/source_code/ZUserVoExtend1ServiceImpl.java b/rest-api-jit-core/src/test/resources/source_code/ZUserVoExtend1ServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..8825eef4d52c4d26d6d09142b460a8d5e4c04543
--- /dev/null
+++ b/rest-api-jit-core/src/test/resources/source_code/ZUserVoExtend1ServiceImpl.java
@@ -0,0 +1,318 @@
+package com.inspur.gs.scm.sd.salesorder.rest;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.inspur.edp.sgf.api.service.ServiceInvoker;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.Objects;
+
+@Data
+public class ZUserVoExtend1ServiceImpl implements ZUserVoExtend1Service {
+
+ protected String voId = "8ac64c96-5c03-45ff-bd0b-0fa2aefa9f90";
+
+ protected String voCode = "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1";
+
+ @Override
+ public String createSession() {
+ return "createSession";
+ }
+
+ @Override
+ public Object create(JsonNode node) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(Objects.isNull(node) ? null : node.get("requestInfo"));
+ jsonNodes.add(Objects.isNull(node) ? null : node.get("defaultValue"));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&Create", jsonNodes);
+ }
+
+ @Override
+ public Object edit(String dataId, JsonNode node) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(node);
+ jsonNodes.add(mapper.valueToTree(dataId));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&Edit", jsonNodes);
+ }
+
+ @Override
+ public void delete() {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&Delete", jsonNodes);
+ }
+
+ @Override
+ public void batchDelete(String ids) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(mapper.valueToTree(ids));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&BatchDelete", jsonNodes);
+ }
+
+ @Override
+ public Object update(JsonNode node) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(Objects.isNull(node) ? null : node.get("requestInfo"));
+ jsonNodes.add(Objects.isNull(node) ? null : node.get("changeDetail"));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&Update", jsonNodes);
+ }
+
+ @Override
+ public Object retrieve(String dataId) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(mapper.valueToTree(dataId));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&Retrieve", jsonNodes);
+ }
+
+ @Override
+ public Object retrieveWithChildPagination(String dataId, JsonNode node) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(Objects.isNull(node) ? null : node.get("requestInfo"));
+ jsonNodes.add(mapper.valueToTree(dataId));
+ jsonNodes.add(Objects.isNull(node) ? null : node.get("retrieveParam"));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&RetrieveWithChildPagination", jsonNodes);
+ }
+
+ @Override
+ public Object queryChild(JsonNode node) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(Objects.isNull(node) ? null : node.get("requestInfo"));
+ jsonNodes.add(Objects.isNull(node) ? null : node.get("nodeCodes"));
+ jsonNodes.add(Objects.isNull(node) ? null : node.get("ids"));
+ jsonNodes.add(Objects.isNull(node) ? null : node.get("pagination"));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&QueryChild", jsonNodes);
+ }
+
+ @Override
+ public Object query(String entityFilter) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(mapper.valueToTree(entityFilter));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&Query", jsonNodes);
+ }
+
+ @Override
+ public Object save(JsonNode node) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(node);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&保存", jsonNodes);
+ }
+
+ @Override
+ public void cancel() {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&Cancel", jsonNodes);
+ }
+
+ @Override
+ public Object getElementHelp(String labelId, String nodeCode, String queryParam) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(mapper.valueToTree(labelId));
+ jsonNodes.add(mapper.valueToTree(nodeCode));
+ jsonNodes.add(mapper.valueToTree(queryParam));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&ElementHelp", jsonNodes);
+ }
+
+ @Override
+ public Object createChildUserDetail(String rootId, JsonNode node) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(node);
+ jsonNodes.add(mapper.valueToTree(rootId));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&CreateChild##UserDetail", jsonNodes);
+ }
+
+ @Override
+ public void deleteChildUserDetail(String rootId, String userDetailId) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(mapper.valueToTree(rootId));
+ jsonNodes.add(mapper.valueToTree(userDetailId));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&DeleteChild##UserDetail", jsonNodes);
+ }
+
+ @Override
+ public Object extend_Delete(JsonNode node) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(Objects.isNull(node) ? null : node.get("requestInfo"));
+ jsonNodes.add(Objects.isNull(node) ? null : node.get("dataId"));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&Extend_Delete", jsonNodes);
+ }
+
+ @Override
+ public Object extend_BatchDelete(String ids, JsonNode node) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(node);
+ jsonNodes.add(mapper.valueToTree(ids));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&Extend_BatchDelete", jsonNodes);
+ }
+
+ @Override
+ public Object extend_Retrieve(String dataId, JsonNode node) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(node);
+ jsonNodes.add(mapper.valueToTree(dataId));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&Extend_Retrieve", jsonNodes);
+ }
+
+ @Override
+ public Object extend_Query(String entityFilter, JsonNode node) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(node);
+ jsonNodes.add(mapper.valueToTree(entityFilter));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&Extend_Query", jsonNodes);
+ }
+
+ @Override
+ public Object extend_GetElementHelp(JsonNode node) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(Objects.isNull(node) ? null : node.get("requestInfo"));
+ jsonNodes.add(Objects.isNull(node) ? null : node.get("labelId"));
+ jsonNodes.add(Objects.isNull(node) ? null : node.get("nodeCode"));
+ jsonNodes.add(Objects.isNull(node) ? null : node.get("queryParam"));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&Extend_ElementHelp", jsonNodes);
+ }
+
+ @Override
+ public Object extend_DeleteChildUserDetail(String rootId, String userDetailId, JsonNode node) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(node);
+ jsonNodes.add(mapper.valueToTree(rootId));
+ jsonNodes.add(mapper.valueToTree(userDetailId));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "c1a294b7-9407-4c6a-bc9c-c56f14210e08&^^&Extend_DeleteChild##UserDetail", jsonNodes);
+ }
+
+ @Override
+ public Object dataDeser(JsonNode node) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(node);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "b8385dd6-f1b7-4e5d-84a6-18a6ceb78396&^^&DataDeser", jsonNodes);
+ }
+
+ @Override
+ public Object submit(JsonNode node) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(node);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "48c6a5ad-7f9b-405e-a623-627254c9bb64&^^&Submit", jsonNodes);
+ }
+
+ @Override
+ public Object t1(JsonNode node) {
+ ArrayList jsonNodes = new ArrayList();
+ ObjectMapper mapper = new ObjectMapper();
+ try {
+ jsonNodes.add(node);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return ServiceInvoker.invokeByJsonNode("VO", "com.inspur.gs.scm.sd.salesorder.vo.ZUserVoExtend1", "358cb06d-7be8-40cb-bc3c-957d4c9359a2&^^&T1", jsonNodes);
+ }
+
+}
\ No newline at end of file
diff --git a/rest-api-jit-core/src/test/resources/var/jit/oldJit/5f88f4a7-ee63-4e00-8cd3-8c5f90c85355/manifest.json b/rest-api-jit-core/src/test/resources/var/jit/oldJit/5f88f4a7-ee63-4e00-8cd3-8c5f90c85355/manifest.json
new file mode 100644
index 0000000000000000000000000000000000000000..dc70332b3955495e7a6d2384fc967d995da9e09b
--- /dev/null
+++ b/rest-api-jit-core/src/test/resources/var/jit/oldJit/5f88f4a7-ee63-4e00-8cd3-8c5f90c85355/manifest.json
@@ -0,0 +1,9 @@
+{
+ "version": "9e495821-3f08-448f-af1f-2476775d0fdb#1.0.2",
+ "compileTime": "2022-09-20T07:47:33.431+00:00",
+ "description": "1.0版本jit生成的缓存",
+ "extendProperties": {
+ "endpointAddress": "/apporder/df/v1.0/merchantform_frm",
+ "className": "com.inspur.gs.apporder.df.merchant.merchant.front.rest.MerchantForm_frmDynamicService"
+ }
+}
\ No newline at end of file
diff --git a/rest-api-jit-spi/pom.xml b/rest-api-jit-spi/pom.xml
index 7d199c026ffb22e9e61f2478a76c6e96a66d21b8..b1554d929272667cf8eb92c913dfae2f4a6259b2 100644
--- a/rest-api-jit-spi/pom.xml
+++ b/rest-api-jit-spi/pom.xml
@@ -21,11 +21,11 @@
rest-api-engine
com.inspur.edp
- 1.0.2
+ 1.0.3-SNAPSHOT
4.0.0
rest-api-jit-spi
-
\ No newline at end of file
+
diff --git a/toout.bat b/toout.bat
new file mode 100644
index 0000000000000000000000000000000000000000..18c1db8664cd0060dadb01f1dd28f24a24b26b7a
--- /dev/null
+++ b/toout.bat
@@ -0,0 +1,14 @@
+@echo off
+
+for /f "tokens=*" %%i in ('CALL .\xpath0.1.bat pom.xml "/project/version"') do set version=%%i
+ECHO version=%version%
+
+DEL /S/Q .\out
+
+MKDIR .\out\server\platform\common\libs
+
+COPY .\rest-api-jit-api\target\rest-api-jit-api-%version%.jar .\out\server\platform\common\libs\jit-api.jar
+COPY .\rest-api-jit-core\target\rest-api-jit-core-%version%.jar .\out\server\platform\common\libs\jit-core.jar
+COPY .\rest-api-jit-spi\target\rest-api-jit-spi-%version%.jar .\out\server\platform\common\libs\jit-spi.jar
+
+::pause
diff --git a/xpath0.1.bat b/xpath0.1.bat
new file mode 100644
index 0000000000000000000000000000000000000000..630822c99a4c6dd92d6db5202d62f437261c920a
--- /dev/null
+++ b/xpath0.1.bat
@@ -0,0 +1,122 @@
+@if (@X)==(@Y) @end /* JScript comment
+ @echo off
+
+ set "file=%~f1"
+ set "xpath=%~2"
+ set "option=%~3"
+ set "node=%~4"
+
+ if "%~2" equ "" (
+ goto :printHelp
+ )
+
+ for %%# in ("-h" "-help" "/h" "/help" "") do (
+ if /i "%~1" equ %%# (
+ goto :printHelp
+ )
+ )
+
+ if exist "%file%\" (
+ echo file "%~1" does not exist
+ exit /b 3
+ )
+ if not exist "%file%" (
+ echo file "%~1" does not exist
+ exit /b 4
+ )
+
+
+ cscript //E:JScript //nologo "%~f0" /file:"%file%" /xpath:"%xpath%" /option:"%option%" /node:%node%
+
+ exit /b %errorlevel%
+
+ :printHelp
+ echo %~nx0 prints the value (or list of values)
+ echo of xml attribute/node in xml by given xpath expression
+ echo(
+ echo Usage:
+ echo(
+ echo call %~nx0 "filePath" "xpathExpression"
+ exit /b %errorlevel%
+
+@if (@X)==(@Y) @end JScript comment */
+
+if (WScript.Arguments.length<2)
+{
+ WScript.Echo("Not enough arguments");
+ WScript.Quit(3);
+}
+
+
+function escapeRegExp(str) {
+ return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
+}
+
+function replaceAll(str, find, replace) {
+ return str.replace(new RegExp(escapeRegExp(find), 'g'), replace);
+}
+
+var xp=WScript.Arguments.Named.Item("xpath");
+xp=replaceAll(xp,"'",'"');
+
+var option=WScript.Arguments.Named.Item("option");
+var node_n=parseInt(WScript.Arguments.Named.Item("node"));
+
+
+var objDoc;
+var objNodes;
+var loaded;
+
+try {
+ objDoc = WScript.CreateObject("MSXML.DOMDocument");
+ loaded=objDoc.load(WScript.Arguments.Named.Item("file"));
+} catch (err){
+ WScript.Echo("Error while parsing the xml");
+ WScript.Echo(err.message);
+ WScript.Quit(1);
+}
+
+if(!loaded){
+ WScript.Echo("Error while parsing the xml");
+ WScript.Echo("");
+ WScript.Echo("Error Code:"+objDoc.parseError.errorCode);
+ WScript.Echo("");
+ WScript.Echo("Line:"+objDoc.parseError.line+" Posotion:"+objDoc.parseError.filepos);
+ WScript.Echo("");
+ WScript.Echo("Reason:"+objDoc.parseError.reason);
+ WScript.Echo("");
+ WScript.Echo("URL:"+objDoc.parseError.url);
+ WScript.Echo("");
+ WScript.Echo(objDoc.parseError.srcText);
+ WScript.Quit(5);
+}
+
+try {
+ var objNodes = objDoc.selectNodes(xp);
+} catch (err){
+ WScript.Echo("invalid xpath expression");
+ WScript.Echo(err.message);
+ WScript.Quit(2);
+}
+
+if(!isNaN(node_n)) {
+ if(node_n>=objNodes.length){
+ WScript.Echo("Out of nodes range");
+ WScript.Echo("Nodes length is: "+objNodes.length);
+ WScript.Quit(35);
+ }
+ if(option.toLowerCase()=="xml"){
+ WScript.Echo(objNodes.item(node_n).xml);
+ } else {
+ WScript.Echo(objNodes.item(node_n).text);
+ }
+} else {
+ for (var i=0;i