执行groovy脚本
1.springboot项目和groovy脚本配合
备注:groovy脚本需要继承springboot项目的基类,并实现固定的方法
2.基类AbstractInspectScript
package com.xxx.inspect.plugin;
import com.alibaba.fastjson.JSON;
import com.xxx.inspect.domain.InspectObject;
import com.xxx.inspect.domain.InspectResult;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.Map;
@Slf4j
public abstract class AbstractInspectScript {
public abstract List<InspectResult> inspect(List<InspectObject> objects, String threshold, Map param);
protected void log(String msg){
log.info(msg);
}
protected String object2string(Object object){
return JSON.toJSONString(object);
}
protected boolean empty(String str){
return str == null || str.isEmpty();
}
}
3.巡检插件
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.http.client.methods.HttpRequestBase;
import com.xxx.inspect.domain.InspectResult;
import com.xxx.inspect.domain.InspectObject;
import com.xxx.inspect.plugin.AbstractInspectScript;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSON;
import java.time.LocalDateTime;
public class SimpleInspectScript extends AbstractInspectScript {
@Override
public List<InspectResult> inspect(List<InspectObject> objects, String threshold, Map param) {
List<InspectResult> result = new ArrayList();
for (obj in objects) {
result.add(new InspectResult(obj.object,true,"概览","详情",true));
}
return result;
}
}
4.调度过程
package com.xxx.inspect.executor;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.xxx.inspect.domain.InspectObject;
import com.xxx.inspect.domain.InspectResult;
import com.xxx.inspect.domain.ScriptExecutionResult;
import com.xxx.inspect.plugin.AbstractInspectScript;
import groovy.lang.GroovyClassLoader;
import lombok.extern.slf4j.Slf4j;
import org.codehaus.groovy.control.CompilationFailedException;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;
@Slf4j
@Component
public class GroovyScriptExecutor {
private final GroovyClassLoader groovyClassLoader = new GroovyClassLoader(this.getClass().getClassLoader());
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
/**
* 已经分好分片,扫描对象不能超200
* @param scriptContent
* @param objects
* @param threshold
* @return
*/
public ScriptExecutionResult executeScript(String scriptContent, List<String> objects, String threshold) {
ScriptExecutionResult result = new ScriptExecutionResult();
List<InspectResult> inspectResults = new ArrayList<>();
if (StringUtils.isBlank(scriptContent)) {
result.setSuccess(false);
result.setError("巡检脚本内容为空");
return result;
}
if (objects.size() > 200) {
result.setSuccess(false);
result.setError("对象数量不能超过200");
return result;
}
// 用于捕获脚本输出的流
ByteArrayOutputStream logStream = new ByteArrayOutputStream();
PrintStream originalOut = System.out;
PrintStream originalErr = System.err;
PrintStream logPrintStream = new PrintStream(logStream);
Future<List<InspectResult>> executionFuture = null;
try {
// 重定向标准输出和错误输出以捕获脚本日志
System.setOut(logPrintStream);
System.setErr(logPrintStream);
// groovy脚本依赖的jar包存储到oss,并数据库中维护到脚本和jar包的关系
// TODO: 将groovy脚本依赖的jar包下载下来放到本地临时目录中,取到其URI 地址
URL[] urls = null;
URLClassLoader scriptClassLoader = new URLClassLoader(urls, parentClassLoader);
GroovyClassLoader groovyClassLoader = new GroovyClassLoader(scriptClassLoader);
Class<?> scriptClass = groovyClassLoader.parseClass(scriptContent);
AbstractInspectScript plugin = (AbstractInspectScript) scriptClass.getDeclaredConstructor().newInstance();
List<InspectObject> inspectionObjects = objects.stream()
.map(object -> new InspectObject(object))
.collect(Collectors.toList());
// 设置上下文类加载器,为了解决groovy脚本中使用外部springboot项目依赖的效果
Thread.currentThread().setContextClassLoader(groovyClassLoader);
// 带超时控制的脚本执行
executionFuture = executorService.submit(() ->
plugin.inspect(inspectionObjects, threshold, null)
);
// 设置执行超时(10秒)
inspectResults = executionFuture.get(20, TimeUnit.MINUTES);
result.setSuccess(true);
result.setResults(inspectResults);
} catch (CompilationFailedException e) {
// 脚本编译错误(语法错误)
result.setSuccess(false);
result.setError("脚本编译失败: " + e.getMessage());
log.error("脚本编译错误", e);
} catch (TimeoutException e) {
// 执行超时
result.setSuccess(false);
result.setError("脚本执行超时(超过20分钟)");
log.error("脚本执行超时", e);
if (executionFuture != null) {
executionFuture.cancel(true);
}
} catch (Exception e) {
// 其他运行时错误
result.setSuccess(false);
result.setError("脚本执行失败: " + e.getMessage());
log.error("脚本执行错误", e);
} finally {
// 恢复标准输出
System.setOut(originalOut);
System.setErr(originalErr);
// 保存捕获的日志
result.setLogs(logStream.toString());
}
return result;
}
}
List
原创:做时间的朋友

浙公网安备 33010602011771号