Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
D
dataease
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
zhu
dataease
Commits
2703e881
提交
2703e881
authored
5月 11, 2021
作者:
fit2cloud-chenyw
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat: 插件管理v0.01
上级
ba678266
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
18 个修改的文件
包含
983 行增加
和
1 行删除
+983
-1
AuthServer.java
...end/src/main/java/io/dataease/auth/server/AuthServer.java
+11
-0
MyPlugin.java
backend/src/main/java/io/dataease/base/domain/MyPlugin.java
+36
-0
MyPluginExample.java
...rc/main/java/io/dataease/base/domain/MyPluginExample.java
+0
-0
MyPluginMapper.java
...src/main/java/io/dataease/base/mapper/MyPluginMapper.java
+31
-0
MyPluginMapper.xml
.../src/main/java/io/dataease/base/mapper/MyPluginMapper.xml
+0
-0
ExtSysPluginMapper.java
.../java/io/dataease/base/mapper/ext/ExtSysPluginMapper.java
+11
-0
ExtSysPluginMapper.xml
...n/java/io/dataease/base/mapper/ext/ExtSysPluginMapper.xml
+27
-0
DeFileUtils.java
.../src/main/java/io/dataease/commons/utils/DeFileUtils.java
+115
-0
ZipUtils.java
...end/src/main/java/io/dataease/commons/utils/ZipUtils.java
+141
-0
SysPluginController.java
.../java/io/dataease/controller/sys/SysPluginController.java
+39
-0
LoadjarUtil.java
...src/main/java/io/dataease/plugins/config/LoadjarUtil.java
+51
-0
SpringContextUtil.java
...in/java/io/dataease/plugins/config/SpringContextUtil.java
+85
-0
ClassloaderResponsity.java
...ava/io/dataease/plugins/loader/ClassloaderResponsity.java
+65
-0
ModuleClassLoader.java
...in/java/io/dataease/plugins/loader/ModuleClassLoader.java
+189
-0
PluginService.java
.../src/main/java/io/dataease/service/sys/PluginService.java
+134
-0
V8__system.sql
backend/src/main/resources/db/migration/V8__system.sql
+32
-0
generatorConfig.xml
backend/src/main/resources/generatorConfig.xml
+1
-1
zh.js
frontend/src/lang/zh.js
+15
-0
没有找到文件。
backend/src/main/java/io/dataease/auth/server/AuthServer.java
浏览文件 @
2703e881
...
...
@@ -13,6 +13,9 @@ import io.dataease.auth.util.RsaUtil;
import
io.dataease.commons.utils.BeanUtils
;
import
io.dataease.commons.utils.CodingUtil
;
import
io.dataease.commons.utils.ServletUtils
;
/*import io.dataease.plugins.config.SpringContextUtil;
import io.dataease.plugins.xpack.dto.response.SysSettingDto;
import io.dataease.plugins.xpack.service.DePluginXpackService;*/
import
org.apache.commons.lang3.ObjectUtils
;
import
org.apache.commons.lang3.StringUtils
;
import
org.apache.shiro.SecurityUtils
;
...
...
@@ -105,6 +108,14 @@ public class AuthServer implements AuthApi {
SysUserEntity
userById
=
authUserService
.
getUserById
(
4L
);
String
nickName
=
userById
.
getNickName
();
System
.
out
.
println
(
nickName
);
/* Map<String, DePluginXpackService> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType(DePluginXpackService.class);
for (Map.Entry entry : beansOfType.entrySet()) {
Object key = entry.getKey();
DePluginXpackService value = (DePluginXpackService)entry.getValue();
List<SysSettingDto> sysSettingDtos = value.systemSettings();
String name = entry.getValue().getClass().getName();
System.out.println("key: "+ key + ", value: "+ name);
}*/
return
"apple"
;
}
}
backend/src/main/java/io/dataease/base/domain/MyPlugin.java
0 → 100644
浏览文件 @
2703e881
package
io
.
dataease
.
base
.
domain
;
import
java.io.Serializable
;
import
lombok.Data
;
@Data
public
class
MyPlugin
implements
Serializable
{
private
Long
pluginId
;
private
String
name
;
private
Boolean
free
;
private
Integer
cost
;
private
String
descript
;
private
String
version
;
private
Integer
installType
;
private
String
creator
;
private
Long
releaseTime
;
private
Long
installTime
;
private
String
moduleName
;
private
String
beanName
;
private
String
icon
;
private
static
final
long
serialVersionUID
=
1L
;
}
\ No newline at end of file
backend/src/main/java/io/dataease/base/domain/MyPluginExample.java
0 → 100644
浏览文件 @
2703e881
差异被折叠。
点击展开。
backend/src/main/java/io/dataease/base/mapper/MyPluginMapper.java
0 → 100644
浏览文件 @
2703e881
package
io
.
dataease
.
base
.
mapper
;
import
io.dataease.base.domain.MyPlugin
;
import
io.dataease.base.domain.MyPluginExample
;
import
java.util.List
;
import
org.apache.ibatis.annotations.Param
;
public
interface
MyPluginMapper
{
long
countByExample
(
MyPluginExample
example
);
int
deleteByExample
(
MyPluginExample
example
);
int
deleteByPrimaryKey
(
Long
pluginId
);
int
insert
(
MyPlugin
record
);
int
insertSelective
(
MyPlugin
record
);
List
<
MyPlugin
>
selectByExample
(
MyPluginExample
example
);
MyPlugin
selectByPrimaryKey
(
Long
pluginId
);
int
updateByExampleSelective
(
@Param
(
"record"
)
MyPlugin
record
,
@Param
(
"example"
)
MyPluginExample
example
);
int
updateByExample
(
@Param
(
"record"
)
MyPlugin
record
,
@Param
(
"example"
)
MyPluginExample
example
);
int
updateByPrimaryKeySelective
(
MyPlugin
record
);
int
updateByPrimaryKey
(
MyPlugin
record
);
}
\ No newline at end of file
backend/src/main/java/io/dataease/base/mapper/MyPluginMapper.xml
0 → 100644
浏览文件 @
2703e881
差异被折叠。
点击展开。
backend/src/main/java/io/dataease/base/mapper/ext/ExtSysPluginMapper.java
0 → 100644
浏览文件 @
2703e881
package
io
.
dataease
.
base
.
mapper
.
ext
;
import
io.dataease.base.domain.MyPlugin
;
import
io.dataease.base.mapper.ext.query.GridExample
;
import
java.util.List
;
public
interface
ExtSysPluginMapper
{
List
<
MyPlugin
>
query
(
GridExample
example
);
}
backend/src/main/java/io/dataease/base/mapper/ext/ExtSysPluginMapper.xml
0 → 100644
浏览文件 @
2703e881
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper
namespace=
"io.dataease.base.mapper.ext.ExtSysPluginMapper"
>
<select
id=
"query"
parameterType=
"io.dataease.base.mapper.ext.query.GridExample"
resultMap=
"io.dataease.base.mapper.MyPluginMapper.BaseResultMap"
>
select *
from my_plugin
<if
test=
"_parameter != null"
>
<include
refid=
"io.dataease.base.mapper.ext.query.GridSql.gridCondition"
/>
</if>
<if
test=
"orderByClause != null"
>
order by ${orderByClause}
</if>
<if
test=
"orderByClause == null"
>
order by install_time desc
</if>
</select>
</mapper>
backend/src/main/java/io/dataease/commons/utils/DeFileUtils.java
0 → 100644
浏览文件 @
2703e881
package
io
.
dataease
.
commons
.
utils
;
import
org.springframework.web.multipart.MultipartFile
;
import
java.io.*
;
import
java.nio.channels.FileChannel
;
public
class
DeFileUtils
{
/**
* Java文件操作 获取不带扩展名的文件名
*/
public
static
String
getFileNameNoEx
(
String
filename
)
{
if
((
filename
!=
null
)
&&
(
filename
.
length
()
>
0
))
{
int
dot
=
filename
.
lastIndexOf
(
'.'
);
if
((
dot
>
-
1
)
&&
(
dot
<
(
filename
.
length
())))
{
return
filename
.
substring
(
0
,
dot
);
}
}
return
filename
;
}
/**
* 获取文件扩展名,不带 .
*/
public
static
String
getExtensionName
(
String
filename
)
{
if
((
filename
!=
null
)
&&
(
filename
.
length
()
>
0
))
{
int
dot
=
filename
.
lastIndexOf
(
'.'
);
if
((
dot
>
-
1
)
&&
(
dot
<
(
filename
.
length
()
-
1
)))
{
return
filename
.
substring
(
dot
+
1
);
}
}
return
filename
;
}
/**
* 将文件名解析成文件的上传路径
*/
public
static
File
upload
(
MultipartFile
file
,
String
filePath
)
{
String
name
=
getFileNameNoEx
(
file
.
getOriginalFilename
());
String
suffix
=
getExtensionName
(
file
.
getOriginalFilename
());
try
{
String
fileName
=
name
+
"."
+
suffix
;
String
path
=
filePath
+
fileName
;
// getCanonicalFile 可解析正确各种路径
File
dest
=
new
File
(
path
).
getCanonicalFile
();
// 检测是否存在目录
if
(!
dest
.
getParentFile
().
exists
())
{
if
(!
dest
.
getParentFile
().
mkdirs
())
{
System
.
out
.
println
(
"was not successful."
);
}
}
// 文件写入
// file.transferTo(dest);
FileOutputStream
fileOutputStream
=
new
FileOutputStream
(
dest
);
fileOutputStream
.
write
(
file
.
getBytes
());
fileOutputStream
.
flush
();
fileOutputStream
.
close
();
return
dest
;
}
catch
(
Exception
e
)
{
LogUtil
.
error
(
e
.
getMessage
(),
e
);
}
return
null
;
}
public
static
String
copy
(
File
source
,
String
targetDir
)
throws
IOException
{
String
name
=
source
.
getName
();
String
destPath
=
null
;
if
(
targetDir
.
endsWith
(
"/"
)
||
targetDir
.
endsWith
(
"\\"
)){
destPath
=
targetDir
+
name
;
}
else
{
destPath
=
targetDir
+
"/"
+
name
;
}
File
DestFile
=
new
File
(
destPath
);
if
(!
DestFile
.
getParentFile
().
exists
())
{
DestFile
.
getParentFile
().
mkdirs
();
}
copyFileUsingFileChannels
(
source
,
DestFile
);
return
destPath
;
}
private
static
void
copyFileUsingFileChannels
(
File
source
,
File
dest
)
throws
IOException
{
FileChannel
inputChannel
=
null
;
FileChannel
outputChannel
=
null
;
try
{
inputChannel
=
new
FileInputStream
(
source
).
getChannel
();
outputChannel
=
new
FileOutputStream
(
dest
).
getChannel
();
outputChannel
.
transferFrom
(
inputChannel
,
0
,
inputChannel
.
size
());
}
finally
{
inputChannel
.
close
();
outputChannel
.
close
();
}
}
public
static
String
readJson
(
File
file
)
{
String
str
=
null
;
try
{
FileReader
fileReader
=
new
FileReader
(
file
);
Reader
reader
=
new
InputStreamReader
(
new
FileInputStream
(
file
),
"utf-8"
);
int
ch
=
0
;
StringBuffer
sb
=
new
StringBuffer
();
while
((
ch
=
reader
.
read
())
!=
-
1
)
{
sb
.
append
((
char
)
ch
);
}
fileReader
.
close
();
reader
.
close
();
str
=
sb
.
toString
();
return
str
;
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
return
null
;
}
}
}
backend/src/main/java/io/dataease/commons/utils/ZipUtils.java
0 → 100644
浏览文件 @
2703e881
package
io
.
dataease
.
commons
.
utils
;
import
java.io.*
;
import
java.util.Enumeration
;
import
java.util.zip.ZipEntry
;
import
java.util.zip.ZipException
;
import
java.util.zip.ZipFile
;
import
java.util.zip.ZipInputStream
;
public
class
ZipUtils
{
/**
* 解压文件
*
* @param zipFilePath 解压文件路径
* @param outputFolder 输出解压文件路径
*/
public
static
void
unZipIt
(
String
zipFilePath
,
String
outputFolder
)
{
byte
[]
buffer
=
new
byte
[
1024
];
File
folder
=
new
File
(
outputFolder
);
if
(!
folder
.
exists
())
{
folder
.
mkdir
();
}
try
{
//get the zip file content
ZipInputStream
zis
=
new
ZipInputStream
(
new
FileInputStream
(
zipFilePath
));
ZipEntry
ze
=
zis
.
getNextEntry
();
while
(
ze
!=
null
)
{
String
fileName
=
ze
.
getName
();
File
newFile
=
new
File
(
outputFolder
+
File
.
separator
+
fileName
);
System
.
out
.
println
(
"file unzip : "
+
newFile
.
getAbsoluteFile
());
//大部分网络上的源码,这里没有判断子目录
if
(
ze
.
isDirectory
())
{
if
(!
newFile
.
mkdirs
())
{
System
.
out
.
println
(
"was not successful."
);
}
}
else
{
if
(!
new
File
(
newFile
.
getParent
()).
mkdirs
())
{
System
.
out
.
println
(
"was not successful."
);
}
FileOutputStream
fos
=
new
FileOutputStream
(
newFile
);
int
len
;
while
((
len
=
zis
.
read
(
buffer
))
!=
-
1
)
{
fos
.
write
(
buffer
,
0
,
len
);
}
fos
.
close
();
}
ze
=
zis
.
getNextEntry
();
}
zis
.
closeEntry
();
zis
.
close
();
System
.
out
.
println
(
"Done"
);
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
}
public
static
void
unzip
(
File
source
,
String
out
)
throws
IOException
{
try
(
ZipInputStream
zis
=
new
ZipInputStream
(
new
FileInputStream
(
source
)))
{
ZipEntry
entry
=
zis
.
getNextEntry
();
while
(
entry
!=
null
)
{
File
file
=
new
File
(
out
,
entry
.
getName
());
if
(
entry
.
isDirectory
())
{
if
(!
file
.
mkdirs
())
{
System
.
out
.
println
(
"was not successful."
);
}
}
else
{
File
parent
=
file
.
getParentFile
();
if
(!
parent
.
exists
())
{
if
(!
parent
.
mkdirs
())
{
System
.
out
.
println
(
"was not successful."
);
}
}
try
(
BufferedOutputStream
bos
=
new
BufferedOutputStream
(
new
FileOutputStream
(
file
)))
{
byte
[]
buffer
=
new
byte
[
Math
.
toIntExact
(
entry
.
getSize
())];
int
location
;
while
((
location
=
zis
.
read
(
buffer
))
!=
-
1
)
{
bos
.
write
(
buffer
,
0
,
location
);
}
}
}
entry
=
zis
.
getNextEntry
();
}
}
}
/**
* 把所有文件都直接解压到指定目录(忽略子文件夹)
*
* @param zipFile
* @param folderPath
* @throws ZipException
* @throws IOException
*/
public
static
void
upZipFile
(
File
zipFile
,
String
folderPath
)
throws
ZipException
,
IOException
{
File
desDir
=
new
File
(
folderPath
);
if
(!
desDir
.
exists
())
{
if
(!
desDir
.
mkdirs
())
{
System
.
out
.
println
(
"was not successful."
);
}
}
ZipFile
zf
=
new
ZipFile
(
zipFile
);
for
(
Enumeration
<?>
entries
=
zf
.
entries
();
entries
.
hasMoreElements
();
)
{
ZipEntry
entry
=
((
ZipEntry
)
entries
.
nextElement
());
InputStream
in
=
zf
.
getInputStream
(
entry
);
File
desFile
=
new
File
(
folderPath
,
java
.
net
.
URLEncoder
.
encode
(
entry
.
getName
(),
"UTF-8"
));
if
(!
desFile
.
exists
())
{
File
fileParentDir
=
desFile
.
getParentFile
();
if
(!
fileParentDir
.
exists
())
{
if
(!
fileParentDir
.
mkdirs
())
{
System
.
out
.
println
(
"was not successful."
);
}
}
}
OutputStream
out
=
new
FileOutputStream
(
desFile
);
byte
[]
buffer
=
new
byte
[
1024
*
1024
];
int
realLength
=
in
.
read
(
buffer
);
while
(
realLength
!=
-
1
)
{
out
.
write
(
buffer
,
0
,
realLength
);
realLength
=
in
.
read
(
buffer
);
}
out
.
close
();
in
.
close
();
}
}
}
backend/src/main/java/io/dataease/controller/sys/SysPluginController.java
0 → 100644
浏览文件 @
2703e881
package
io
.
dataease
.
controller
.
sys
;
import
com.github.pagehelper.Page
;
import
com.github.pagehelper.PageHelper
;
import
io.dataease.base.domain.MyPlugin
;
import
io.dataease.commons.utils.PageUtils
;
import
io.dataease.commons.utils.Pager
;
import
io.dataease.controller.sys.base.BaseGridRequest
;
import
io.dataease.service.sys.PluginService
;
import
io.swagger.annotations.Api
;
import
io.swagger.annotations.ApiOperation
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.bind.annotation.*
;
import
org.springframework.web.multipart.MultipartFile
;
import
java.util.List
;
import
java.util.Map
;
@RestController
@Api
(
tags
=
"系统:插件管理"
)
@RequestMapping
(
"/api/plugin"
)
public
class
SysPluginController
{
@Autowired
private
PluginService
pluginService
;
@ApiOperation
(
"查询已安装插件"
)
@PostMapping
(
"/pluginGrid/{goPage}/{pageSize}"
)
public
Pager
<
List
<
MyPlugin
>>
pluginGrid
(
@PathVariable
int
goPage
,
@PathVariable
int
pageSize
,
@RequestBody
BaseGridRequest
request
)
{
Page
<
Object
>
page
=
PageHelper
.
startPage
(
goPage
,
pageSize
,
true
);
return
PageUtils
.
setPageInfo
(
page
,
pluginService
.
query
(
request
));
}
@PostMapping
(
"upload"
)
public
Map
<
String
,
Object
>
localUpload
(
@RequestParam
(
"file"
)
MultipartFile
file
)
throws
Exception
{
return
pluginService
.
localInstall
(
file
);
}
}
backend/src/main/java/io/dataease/plugins/config/LoadjarUtil.java
0 → 100644
浏览文件 @
2703e881
package
io
.
dataease
.
plugins
.
config
;
import
io.dataease.plugins.loader.ClassloaderResponsity
;
import
io.dataease.plugins.loader.ModuleClassLoader
;
import
org.springframework.stereotype.Component
;
import
java.io.File
;
import
java.io.IOException
;
import
java.net.MalformedURLException
;
import
java.net.URI
;
import
java.net.URL
;
import
java.util.List
;
import
java.util.Map
;
@Component
public
class
LoadjarUtil
{
public
List
<?>
loadJar
(
String
jarPath
){
File
jar
=
new
File
(
jarPath
);
URI
uri
=
jar
.
toURI
();
String
moduleName
=
jarPath
.
substring
(
jarPath
.
lastIndexOf
(
"/"
)+
1
,
jarPath
.
lastIndexOf
(
"."
));
try
{
if
(
ClassloaderResponsity
.
getInstance
().
containsClassLoader
(
moduleName
)){
ClassloaderResponsity
.
getInstance
().
removeClassLoader
(
moduleName
);
}
ModuleClassLoader
classLoader
=
new
ModuleClassLoader
(
new
URL
[]{
uri
.
toURL
()},
Thread
.
currentThread
().
getContextClassLoader
());
SpringContextUtil
.
getBeanFactory
().
setBeanClassLoader
(
classLoader
);
Thread
.
currentThread
().
setContextClassLoader
(
classLoader
);
classLoader
.
initBean
();
ClassloaderResponsity
.
getInstance
().
addClassLoader
(
moduleName
,
classLoader
);
}
catch
(
MalformedURLException
e
)
{
e
.
printStackTrace
();
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
return
SpringContextUtil
.
getAllBean
();
}
public
List
<
Map
<
String
,
Object
>>
deleteModule
(
String
moduleName
){
if
(
ClassloaderResponsity
.
getInstance
().
containsClassLoader
(
moduleName
)){
ClassloaderResponsity
.
getInstance
().
removeClassLoader
(
moduleName
);
}
return
beans
();
}
public
List
<
Map
<
String
,
Object
>>
beans
(){
return
SpringContextUtil
.
getAllBean
();
}
}
backend/src/main/java/io/dataease/plugins/config/SpringContextUtil.java
0 → 100644
浏览文件 @
2703e881
package
io
.
dataease
.
plugins
.
config
;
import
org.springframework.beans.BeansException
;
import
org.springframework.beans.factory.support.DefaultListableBeanFactory
;
import
org.springframework.context.ApplicationContext
;
import
org.springframework.context.ApplicationContextAware
;
import
org.springframework.stereotype.Component
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
@Component
public
class
SpringContextUtil
implements
ApplicationContextAware
{
//获取bean工厂,用来实现动态注入bean
//不能使用其他类加载器加载bean
//否则会出现异常:类未找到,类未定义
public
static
DefaultListableBeanFactory
getBeanFactory
(){
return
(
DefaultListableBeanFactory
)
getApplicationContext
().
getAutowireCapableBeanFactory
();
}
public
static
List
<
Map
<
String
,
Object
>>
getAllBean
()
{
List
<
Map
<
String
,
Object
>>
list
=
new
ArrayList
<>();
String
[]
beans
=
getApplicationContext
()
.
getBeanDefinitionNames
();
for
(
String
beanName
:
beans
)
{
Class
<?>
beanType
=
getApplicationContext
()
.
getType
(
beanName
);
Map
<
String
,
Object
>
map
=
new
HashMap
<>();
map
.
put
(
"BeanName"
,
beanName
);
map
.
put
(
"beanType"
,
beanType
);
map
.
put
(
"package"
,
beanType
.
getPackage
());
list
.
add
(
map
);
}
return
list
;
}
/**
* 上下文对象实例
*/
private
static
ApplicationContext
applicationContext
;
public
void
setApplicationContext
(
ApplicationContext
applicationContext
)
throws
BeansException
{
this
.
applicationContext
=
applicationContext
;
}
/**
* 获取applicationContext
*
* @return
*/
public
static
ApplicationContext
getApplicationContext
()
{
return
applicationContext
;
}
public
static
Object
getBean
(
String
name
)
{
return
getApplicationContext
().
getBean
(
name
);
}
public
static
<
T
>
T
getBean
(
Class
<
T
>
clazz
)
{
return
getApplicationContext
().
getBean
(
clazz
);
}
public
static
<
T
>
T
getBean
(
String
name
,
Class
<
T
>
clazz
)
{
return
getApplicationContext
().
getBean
(
name
,
clazz
);
}
}
backend/src/main/java/io/dataease/plugins/loader/ClassloaderResponsity.java
0 → 100644
浏览文件 @
2703e881
package
io
.
dataease
.
plugins
.
loader
;
import
io.dataease.plugins.config.SpringContextUtil
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
java.io.IOException
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.concurrent.ConcurrentHashMap
;
public
class
ClassloaderResponsity
{
private
static
Logger
logger
=
LoggerFactory
.
getLogger
(
ClassloaderResponsity
.
class
);
private
ClassloaderResponsity
(){}
private
Map
<
String
,
ModuleClassLoader
>
responsityMap
=
new
ConcurrentHashMap
<>();
public
void
addClassLoader
(
String
moduleName
,
ModuleClassLoader
moduleClassLoader
){
responsityMap
.
put
(
moduleName
,
moduleClassLoader
);
}
public
boolean
containsClassLoader
(
String
key
){
return
responsityMap
.
containsKey
(
key
);
}
public
ModuleClassLoader
getClassLoader
(
String
key
){
return
responsityMap
.
get
(
key
);
}
public
void
removeClassLoader
(
String
moduleName
){
ModuleClassLoader
moduleClassLoader
=
responsityMap
.
get
(
moduleName
);
try
{
List
<
String
>
registeredBean
=
moduleClassLoader
.
getRegisteredBean
();
for
(
String
beanName
:
registeredBean
)
{
logger
.
info
(
"删除bean:"
+
beanName
);
SpringContextUtil
.
getBeanFactory
().
removeBeanDefinition
(
beanName
);
}
moduleClassLoader
.
close
();
responsityMap
.
remove
(
moduleName
);
}
catch
(
IOException
e
)
{
logger
.
error
(
"删除"
+
moduleName
+
"模块发生错误"
);
}
}
private
static
class
ClassloaderResponsityHodler
{
private
static
ClassloaderResponsity
instamce
=
new
ClassloaderResponsity
();
}
public
static
ClassloaderResponsity
getInstance
(){
return
ClassloaderResponsityHodler
.
instamce
;
}
}
backend/src/main/java/io/dataease/plugins/loader/ModuleClassLoader.java
0 → 100644
浏览文件 @
2703e881
package
io
.
dataease
.
plugins
.
loader
;
import
io.dataease.plugins.config.SpringContextUtil
;
import
org.springframework.beans.factory.config.BeanDefinition
;
import
org.springframework.beans.factory.support.BeanDefinitionBuilder
;
import
org.springframework.stereotype.Component
;
import
org.springframework.stereotype.Repository
;
import
org.springframework.stereotype.Service
;
import
org.springframework.util.StringUtils
;
import
java.io.ByteArrayOutputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.lang.reflect.Modifier
;
import
java.net.URL
;
import
java.net.URLClassLoader
;
import
java.util.*
;
import
java.util.jar.JarEntry
;
import
java.util.jar.JarFile
;
public
class
ModuleClassLoader
extends
URLClassLoader
{
//属于本类加载器加载的jar包
private
JarFile
jarFile
;
//保存已经加载过的Class对象
private
Map
<
String
,
Class
>
cacheClassMap
=
new
HashMap
<>();
//保存本类加载器加载的class字节码
private
Map
<
String
,
byte
[]>
classBytesMap
=
new
HashMap
<>();
//需要注册的spring bean的name集合
private
List
<
String
>
registeredBean
=
new
ArrayList
<>();
//构造
public
ModuleClassLoader
(
URL
[]
urls
,
ClassLoader
parent
)
{
super
(
urls
,
parent
);
URL
url
=
urls
[
0
];
String
path
=
url
.
getPath
();
try
{
jarFile
=
new
JarFile
(
path
);
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
//初始化类加载器执行类加载
init
();
}
//重写loadClass方法
//改写loadClass方式
@Override
public
Class
<?>
loadClass
(
String
name
)
throws
ClassNotFoundException
{
if
(
findLoadedClass
(
name
)==
null
){
return
super
.
loadClass
(
name
);
}
else
{
return
cacheClassMap
.
get
(
name
);
}
}
/**
* 方法描述 初始化类加载器,保存字节码
* @method init
*/
private
void
init
()
{
//解析jar包每一项
Enumeration
<
JarEntry
>
en
=
jarFile
.
entries
();
InputStream
input
=
null
;
try
{
while
(
en
.
hasMoreElements
())
{
JarEntry
je
=
en
.
nextElement
();
String
name
=
je
.
getName
();
//这里添加了路径扫描限制
if
(
name
.
endsWith
(
".class"
))
{
String
className
=
name
.
replace
(
".class"
,
""
).
replaceAll
(
"/"
,
"."
);
input
=
jarFile
.
getInputStream
(
je
);
ByteArrayOutputStream
baos
=
new
ByteArrayOutputStream
();
int
bufferSize
=
4096
;
byte
[]
buffer
=
new
byte
[
bufferSize
];
int
bytesNumRead
=
0
;
while
((
bytesNumRead
=
input
.
read
(
buffer
))
!=
-
1
)
{
baos
.
write
(
buffer
,
0
,
bytesNumRead
);
}
byte
[]
classBytes
=
baos
.
toByteArray
();
classBytesMap
.
put
(
className
,
classBytes
);
}
}
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
finally
{
if
(
input
!=
null
){
try
{
input
.
close
();
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
}
}
//将jar中的每一个class字节码进行Class载入
for
(
Map
.
Entry
<
String
,
byte
[]>
entry
:
classBytesMap
.
entrySet
())
{
String
key
=
entry
.
getKey
();
Class
<?>
aClass
=
null
;
try
{
aClass
=
loadClass
(
key
);
}
catch
(
ClassNotFoundException
e
)
{
e
.
printStackTrace
();
}
cacheClassMap
.
put
(
key
,
aClass
);
}
}
/**
* 方法描述 初始化spring bean
* @method initBean
*/
public
void
initBean
(){
for
(
Map
.
Entry
<
String
,
Class
>
entry
:
cacheClassMap
.
entrySet
())
{
String
className
=
entry
.
getKey
();
Class
<?>
cla
=
entry
.
getValue
();
if
(
isSpringBeanClass
(
cla
)){
BeanDefinitionBuilder
beanDefinitionBuilder
=
BeanDefinitionBuilder
.
genericBeanDefinition
(
cla
);
BeanDefinition
beanDefinition
=
beanDefinitionBuilder
.
getRawBeanDefinition
();
//设置当前bean定义对象是单利的
beanDefinition
.
setScope
(
"singleton"
);
//将变量首字母置小写
String
beanName
=
StringUtils
.
uncapitalize
(
className
);
beanName
=
beanName
.
substring
(
beanName
.
lastIndexOf
(
"."
)+
1
);
beanName
=
StringUtils
.
uncapitalize
(
beanName
);
SpringContextUtil
.
getBeanFactory
().
registerBeanDefinition
(
beanName
,
beanDefinition
);
registeredBean
.
add
(
beanName
);
System
.
out
.
println
(
"注册bean:"
+
beanName
);
}
}
}
//获取当前类加载器注册的bean
//在移除当前类加载器的时候需要手动删除这些注册的bean
public
List
<
String
>
getRegisteredBean
()
{
return
registeredBean
;
}
/**
* 方法描述 判断class对象是否带有spring的注解
* @method isSpringBeanClass
* @param cla jar中的每一个class
* @return true 是spring bean false 不是spring bean
*/
public
boolean
isSpringBeanClass
(
Class
<?>
cla
){
if
(
cla
==
null
){
return
false
;
}
//是否是接口
if
(
cla
.
isInterface
()){
return
false
;
}
//是否是抽象类
if
(
Modifier
.
isAbstract
(
cla
.
getModifiers
())){
return
false
;
}
if
(
cla
.
getAnnotation
(
Component
.
class
)!=
null
){
return
true
;
}
if
(
cla
.
getAnnotation
(
Repository
.
class
)!=
null
){
return
true
;
}
if
(
cla
.
getAnnotation
(
Service
.
class
)!=
null
){
return
true
;
}
return
false
;
}
}
backend/src/main/java/io/dataease/service/sys/PluginService.java
0 → 100644
浏览文件 @
2703e881
package
io
.
dataease
.
service
.
sys
;
import
com.google.gson.Gson
;
import
io.dataease.base.domain.MyPlugin
;
import
io.dataease.base.mapper.MyPluginMapper
;
import
io.dataease.base.mapper.ext.ExtSysPluginMapper
;
import
io.dataease.base.mapper.ext.query.GridExample
;
import
io.dataease.commons.utils.BeanUtils
;
import
io.dataease.commons.utils.DeFileUtils
;
import
io.dataease.commons.utils.ZipUtils
;
import
io.dataease.controller.sys.base.BaseGridRequest
;
import
io.dataease.plugins.config.LoadjarUtil
;
import
org.apache.commons.lang3.ArrayUtils
;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.stereotype.Service
;
import
org.springframework.web.multipart.MultipartFile
;
import
javax.annotation.Resource
;
import
java.io.File
;
import
java.io.IOException
;
import
java.lang.reflect.InvocationTargetException
;
import
java.util.List
;
import
java.util.Map
;
@Service
public
class
PluginService
{
@Value
(
"${dataease.plugin.dir:/opt/dataease/plugins/}"
)
private
String
pluginDir
;
private
final
static
String
pluginJsonName
=
"plugin.json"
;
@Resource
private
ExtSysPluginMapper
extSysPluginMapper
;
@Resource
private
MyPluginMapper
myPluginMapper
;
@Resource
private
LoadjarUtil
loadjarUtil
;
public
List
<
MyPlugin
>
query
(
BaseGridRequest
request
)
{
GridExample
gridExample
=
request
.
convertExample
();
List
<
MyPlugin
>
results
=
extSysPluginMapper
.
query
(
gridExample
);
return
results
;
}
/**
* 从本地安装处插件
* @param file
* @return
*/
public
Map
<
String
,
Object
>
localInstall
(
MultipartFile
file
)
{
//1.上传文件到服务器pluginDir目录下
File
dest
=
DeFileUtils
.
upload
(
file
,
pluginDir
+
"temp/"
);
//2.解压目标文件dest 得到plugin.json和jar
String
folder
=
pluginDir
+
"folder/"
;
try
{
ZipUtils
.
upZipFile
(
dest
,
folder
);
}
catch
(
IOException
e
)
{
// 需要删除文件
e
.
printStackTrace
();
}
//3.解析plugin.json 失败则 直接返回错误 删除文件
File
folderFile
=
new
File
(
folder
);
File
[]
jsonFiles
=
folderFile
.
listFiles
(
this
::
isPluginJson
);
if
(
ArrayUtils
.
isEmpty
(
jsonFiles
))
{
throw
new
RuntimeException
(
"缺少插件描述文件"
);
}
MyPlugin
myPlugin
=
formatJsonFile
(
jsonFiles
[
0
]);
//4.加载jar包 失败则 直接返回错误 删除文件
File
[]
jarFiles
=
folderFile
.
listFiles
(
this
::
isPluginJar
);
if
(
ArrayUtils
.
isEmpty
(
jarFiles
))
{
throw
new
RuntimeException
(
"缺少插件jar文件"
);
}
File
jarFile
=
jarFiles
[
0
];
String
jarRoot
=
pluginDir
+
"jar/"
;
String
jarPath
=
null
;
try
{
jarPath
=
DeFileUtils
.
copy
(
jarFile
,
jarRoot
);
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
loadjarUtil
.
loadJar
(
jarPath
);
//5.写表到my_plugin
myPlugin
.
setPluginId
(
0L
);
myPluginMapper
.
insert
(
myPlugin
);
return
null
;
}
//判断当前文件是否实插件描述文件
//文件名称必须plugin.json
private
boolean
isPluginJson
(
File
file
)
{
return
StringUtils
.
equals
(
file
.
getName
(),
pluginJsonName
);
}
private
boolean
isPluginJar
(
File
file
)
{
String
name
=
file
.
getName
();
return
StringUtils
.
equals
(
DeFileUtils
.
getExtensionName
(
name
),
"jar"
);
}
/**
* 从plugin.json文件反序列化为MyPlugin实例对象
* @return
*/
private
MyPlugin
formatJsonFile
(
File
file
)
{
String
str
=
DeFileUtils
.
readJson
(
file
);
Gson
gson
=
new
Gson
();
Map
<
String
,
Object
>
myPlugin
=
gson
.
fromJson
(
str
,
Map
.
class
);
myPlugin
.
put
(
"free"
,
(
Double
)
myPlugin
.
get
(
"free"
)
>
0.0
);
MyPlugin
result
=
new
MyPlugin
();
try
{
org
.
apache
.
commons
.
beanutils
.
BeanUtils
.
populate
(
result
,
myPlugin
);
}
catch
(
IllegalAccessException
e
)
{
e
.
printStackTrace
();
}
catch
(
InvocationTargetException
e
)
{
e
.
printStackTrace
();
}
//BeanUtils.copyBean(result, myPlugin);
return
result
;
}
/**
* 从插件商城远程安装插件
* 2.0版本实现
* @param params
* @return
*/
public
Map
<
String
,
Object
>
remoteInstall
(
Map
<
String
,
Object
>
params
)
{
return
null
;
}
}
backend/src/main/resources/db/migration/V8__system.sql
浏览文件 @
2703e881
...
...
@@ -239,3 +239,35 @@ INSERT INTO `sys_users_roles` VALUES (19, 4);
COMMIT
;
SET
FOREIGN_KEY_CHECKS
=
1
;
SET
NAMES
utf8mb4
;
SET
FOREIGN_KEY_CHECKS
=
0
;
-- ----------------------------
-- Table structure for my_plugin
-- ----------------------------
DROP
TABLE
IF
EXISTS
`my_plugin`
;
CREATE
TABLE
`my_plugin`
(
`plugin_id`
bigint
(
20
)
NOT
NULL
COMMENT
'主键'
,
`name`
varchar
(
255
)
DEFAULT
NULL
COMMENT
'插件名称'
,
`free`
tinyint
(
1
)
DEFAULT
NULL
COMMENT
'是否免费'
,
`cost`
int
(
10
)
DEFAULT
NULL
COMMENT
'费用'
,
`descript`
varchar
(
255
)
DEFAULT
NULL
COMMENT
'描述'
,
`version`
varchar
(
255
)
DEFAULT
NULL
COMMENT
'版本号'
,
`install_type`
int
(
4
)
DEFAULT
NULL
COMMENT
'安装类型'
,
`creator`
varchar
(
255
)
DEFAULT
NULL
COMMENT
'开发者'
,
`release_time`
bigint
(
13
)
DEFAULT
NULL
COMMENT
'发布时间'
,
`install_time`
bigint
(
13
)
DEFAULT
NULL
COMMENT
'安装时间'
,
`module_name`
varchar
(
255
)
DEFAULT
NULL
COMMENT
'jar包名称'
,
`bean_name`
varchar
(
40
)
DEFAULT
NULL
COMMENT
'bean名称'
,
`icon`
varchar
(
255
)
DEFAULT
NULL
COMMENT
'图标'
,
PRIMARY
KEY
(
`plugin_id`
)
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=
utf8
;
-- ----------------------------
-- Records of my_plugin
-- ----------------------------
BEGIN
;
COMMIT
;
SET
FOREIGN_KEY_CHECKS
=
1
;
backend/src/main/resources/generatorConfig.xml
浏览文件 @
2703e881
...
...
@@ -67,7 +67,7 @@
<!-- <table tableName="datasource"/>-->
<!-- <table tableName="sys_dict"/>-->
<!-- <table tableName="sys_dict_item"/>-->
<table
tableName=
"
dataset_table_field
"
/>
<table
tableName=
"
my_plugin
"
/>
<!-- <table tableName="panel_design"/>-->
...
...
frontend/src/lang/zh.js
浏览文件 @
2703e881
...
...
@@ -887,5 +887,20 @@ export default {
back
:
'返回'
,
view
:
'视图'
,
edit
:
'编辑'
},
plugin
:
{
local_install
:
'本地安装'
,
remote_install
:
'远程安装'
,
name
:
'插件名称'
,
free
:
'是否免费'
,
cost
:
'费用'
,
descript
:
'描述'
,
version
:
'版本'
,
creator
:
'作者'
,
install_time
:
'安装时间'
,
release_time
:
'时间'
,
un_install
:
'卸载'
,
uninstall_confirm
:
'确定卸载该插件'
,
uninstall_cancel
:
'取消卸载插件'
}
}
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论