diff --git a/o2server/configSample/manifest.cfg b/o2server/configSample/manifest.cfg index a7d56c3eb453800a0f95527ed379dc9693cb937b..cc35f009db5b28e713e874239f00762e17868236 100644 --- a/o2server/configSample/manifest.cfg +++ b/o2server/configSample/manifest.cfg @@ -1,5 +1,5 @@ {"node_127.0.0.1.json":"节点配置", - "appStyle.json":"应用样式配置", + "appStyle.json":"移动端应用样式配置", "centerServer.json":"中心服务配置", "clientInit.json":"客户端初始化配置", "collect.json":"连接到云平台配置", @@ -7,8 +7,7 @@ "components.json":"组件配置", "dingding.json":"钉钉配置", "dumpRestoreData.json":"导出导入数据配置", - "dumpRestoreStorage.json":"导出导入文件配置", - "exmail.json":"邮件配置", + "exmail.json":"腾讯企业邮邮件配置", "externalDataSources.json":"数据库配置", "externalDataSources_db2.json":"db2数据库配置", "externalDataSources_dm.json":"达梦数据库配置", @@ -28,11 +27,10 @@ "person.json":"个人信息配置", "portal.json":"门户配置", "processPlatform.json":"流程平台配置", - "pushConfig.json":"极光推送配置", - "qiyeweixin.json":"微信配置", - "query.json":"定时任务配置", - "token.json":"sso配置", - "vfs.json":"附件上传配置", + "qiyeweixin.json":"企业微信配置", + "query.json":"数据中心配置", + "token.json":"令牌,密钥配置", + "vfs.json":"虚拟文件存储配置", "welink.json":"华为WeLink配置", "workTime.json":"工作时间配置", "zhengwuDingding.json":"政务钉钉配置" diff --git a/o2server/x_console/src/main/java/com/x/server/console/NodeAgent.java b/o2server/x_console/src/main/java/com/x/server/console/NodeAgent.java index a02b9ea175a6bf6f60f9588efc0331aeb8b4d0f1..f3169707f80c02f32833c181727cef944d5c6e62 100644 --- a/o2server/x_console/src/main/java/com/x/server/console/NodeAgent.java +++ b/o2server/x_console/src/main/java/com/x/server/console/NodeAgent.java @@ -3,16 +3,32 @@ package com.x.server.console; import java.io.*; import java.net.ServerSocket; import java.net.Socket; +import java.net.URL; +import java.net.URLClassLoader; import java.nio.charset.StandardCharsets; +import java.nio.file.FileVisitOption; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.*; import java.util.concurrent.LinkedBlockingQueue; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Stream; + +import javax.servlet.DispatcherType; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathFactory; import com.x.base.core.project.tools.*; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.filefilter.FileFilterUtils; +import org.apache.commons.io.filefilter.IOFileFilter; import org.apache.commons.io.filefilter.WildcardFileFilter; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.BooleanUtils; @@ -22,7 +38,13 @@ import org.eclipse.jetty.quickstart.QuickStartWebApp; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.server.handler.gzip.GzipHandler; +import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlet.ServletHolder; +import org.w3c.dom.Document; +import com.alibaba.druid.support.http.StatViewServlet; +import com.alibaba.druid.support.http.WebStatFilter; +import com.x.base.core.project.x_base_core_project; import com.x.base.core.project.annotation.Module; import com.x.base.core.project.annotation.ModuleType; import com.x.base.core.project.config.Config; @@ -31,6 +53,7 @@ import com.x.base.core.project.logger.Logger; import com.x.base.core.project.logger.LoggerFactory; import com.x.server.console.action.ActionCreateEncryptKey; import com.x.server.console.server.Servers; +import com.x.server.console.server.application.ApplicationServerTools; import io.github.classgraph.ClassGraph; import io.github.classgraph.ClassInfo; @@ -44,7 +67,9 @@ public class NodeAgent extends Thread { } public static final Pattern redeploy_pattern = Pattern.compile("^redeploy:(.+)$", Pattern.CASE_INSENSITIVE); - + + public static final Pattern uninstall_pattern = Pattern.compile("^uninstall:(.+)$", Pattern.CASE_INSENSITIVE); + public static final Pattern syncFile_pattern = Pattern.compile("^syncFile:(.+)$", Pattern.CASE_INSENSITIVE); public static final Pattern upload_resource_pattern = Pattern.compile("^uploadResource:(.+)$", @@ -111,6 +136,34 @@ public class NodeAgent extends Thread { } + matcher = uninstall_pattern.matcher(commandObject.getCommand()); + if (matcher.find()) { + String strCommand = commandObject.getCommand(); + strCommand = strCommand.trim(); + strCommand = strCommand.substring(strCommand.indexOf(":") + 1, strCommand.length()); + logger.info("收接到命令:" + strCommand); + String filename = dis.readUTF(); + File tempFile = null; + switch (strCommand) { + case "customWar": + tempFile = Config.dir_custom(); + break; + case "customJar": + tempFile = Config.dir_custom_jars(); + break; + } + logger.info("文件名path:" + tempFile.getAbsolutePath() + File.separator + filename); + File file = new File(tempFile.getAbsolutePath() + File.separator + filename); + + filename = filename.substring(0, filename.lastIndexOf(".")); + // uninstall + boolean result = this.customWarUninstall(filename); + + logger.info("uninstall:" + result); + continue; + } + + matcher = redeploy_pattern.matcher(commandObject.getCommand()); if (matcher.find()) { String strCommand = commandObject.getCommand(); @@ -489,10 +542,34 @@ public class NodeAgent extends Thread { } } + private boolean customWarUninstall(String simpleName) throws Exception { + boolean stop = false; + File war = new File(Config.dir_custom(true), simpleName + ".war"); + //File dir = new File(Config.dir_servers_applicationServer_work(), simpleName); + if (Servers.applicationServerIsRunning()) { + GzipHandler gzipHandler = (GzipHandler) Servers.applicationServer.getHandler(); + HandlerList hanlderList = (HandlerList) gzipHandler.getHandler(); + for (Handler handler : hanlderList.getHandlers()) { + if (QuickStartWebApp.class.isAssignableFrom(handler.getClass())) { + QuickStartWebApp app = (QuickStartWebApp) handler; + if (StringUtils.equals("/" + simpleName, app.getContextPath())) { + app.stop(); + app.destroy(); + stop = true; + war.delete(); + logger.print("{} need stop.", app.getDisplayName()); + } + } + } + } + return stop; + } + private void customWar(String simpleName, byte[] bytes,boolean rebootApp) throws Exception { File war = new File(Config.dir_custom(true), simpleName + ".war"); File dir = new File(Config.dir_servers_applicationServer_work(), simpleName); FileUtils.writeByteArrayToFile(war, bytes, false); + boolean isStartApplication = false;//第一次上传 if (Servers.applicationServerIsRunning()) { GzipHandler gzipHandler = (GzipHandler) Servers.applicationServer.getHandler(); HandlerList hanlderList = (HandlerList) gzipHandler.getHandler(); @@ -501,17 +578,60 @@ public class NodeAgent extends Thread { QuickStartWebApp app = (QuickStartWebApp) handler; if (StringUtils.equals("/" + simpleName, app.getContextPath())) { app.stop(); + logger.print("{} need restart because {} redeployed.", app.getDisplayName(), simpleName); this.modified(bytes, war, dir); app.start(); + isStartApplication = true; } } } if(rebootApp) { - Servers.stopApplicationServer(); - Thread.sleep(3000); - Servers.startApplicationServer(); + if(!isStartApplication) { + customWarPublish(simpleName); + } + } + } + } + + private void customWarPublish(String name) throws Exception { + File war = new File(Config.dir_custom(), name + ".war"); + File dir = new File(Config.dir_servers_applicationServer_work(), name); + if (war.exists()) { + modified(war, dir); + String className = contextParamProject(dir); + URLClassLoader classLoader = new URLClassLoader( + new URL[] { new File(dir, "WEB-INF/classes").toURI().toURL() }); + Class cls = classLoader.loadClass(className); + QuickStartWebApp webApp = new QuickStartWebApp(); + webApp.setAutoPreconfigure(false); + webApp.setDisplayName(name); + webApp.setContextPath("/" + name); + webApp.setResourceBase(dir.getAbsolutePath()); + webApp.setDescriptor(dir + "/WEB-INF/web.xml"); + webApp.setExtraClasspath(calculateExtraClassPath(cls)); + webApp.getInitParams().put("org.eclipse.jetty.servlet.Default.useFileMappedBuffer", "false"); + webApp.getInitParams().put("org.eclipse.jetty.jsp.precompiled", "true"); + webApp.getInitParams().put("org.eclipse.jetty.servlet.Default.dirAllowed", "false"); + + /* stat */ + if (BooleanUtils.isTrue(Config.currentNode().getApplication().getStatEnable())) { + FilterHolder statFilterHolder = new FilterHolder(new WebStatFilter()); + statFilterHolder.setInitParameter("exclusions", Config.currentNode().getApplication().getStatExclusions()); + webApp.addFilter(statFilterHolder, "/*", EnumSet.of(DispatcherType.REQUEST)); + ServletHolder statServletHolder = new ServletHolder(StatViewServlet.class); + statServletHolder.setInitParameter("sessionStatEnable", "false"); + webApp.addServlet(statServletHolder, "/druid/*"); } + /* stat end */ + logger.print("addHandler {} ", webApp.getDisplayName()); + + GzipHandler gzipHandler = (GzipHandler) Servers.applicationServer.getHandler(); + HandlerList hanlderList = (HandlerList) gzipHandler.getHandler(); + hanlderList.addHandler(webApp); + webApp.stop(); + logger.print("{} need restart because {} redeployed.", webApp.getDisplayName(), name); + webApp.start(); } } @@ -519,6 +639,7 @@ public class NodeAgent extends Thread { File jar = new File(Config.dir_custom_jars(true), simpleName + ".jar"); FileUtils.writeByteArrayToFile(jar, bytes, false); List contexts = new ArrayList<>(); + boolean isStartApplication = false; for (String s : Config.dir_custom().list(new WildcardFileFilter("*.war"))) { contexts.add("/" + FilenameUtils.getBaseName(s)); } @@ -530,16 +651,20 @@ public class NodeAgent extends Thread { QuickStartWebApp app = (QuickStartWebApp) handler; if (contexts.contains(app.getContextPath()) ) { app.stop(); + logger.print("{} need restart because {} redeployed.", app.getDisplayName(), simpleName); Thread.sleep(3000); app.start(); + isStartApplication = true; } } } if(rebootApp) { + if(!isStartApplication) { Servers.stopApplicationServer(); Thread.sleep(1000); Servers.startApplicationServer(); + } } } } @@ -588,7 +713,88 @@ public class NodeAgent extends Thread { FileUtils.writeStringToFile(lastModified, war.lastModified() + "", DefaultCharset.charset_utf_8, false); } } - + + private static void modified(File war, File dir) throws IOException { + File lastModified = new File(dir, "WEB-INF/lastModified"); + if ((!lastModified.exists()) || lastModified.isDirectory() || (war.lastModified() != NumberUtils + .toLong(FileUtils.readFileToString(lastModified, DefaultCharset.charset_utf_8), 0))) { + if (dir.exists()) { + FileUtils.forceDelete(dir); + } + JarTools.unjar(war, "", dir, true); + FileUtils.writeStringToFile(lastModified, war.lastModified() + "", DefaultCharset.charset_utf_8, false); + } + } + + private static String contextParamProject(File dir) throws Exception { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder + .parse(new ByteArrayInputStream(FileUtils.readFileToByteArray(new File(dir, "WEB-INF/web.xml")))); + XPathFactory xPathfactory = XPathFactory.newInstance(); + XPath xpath = xPathfactory.newXPath(); + XPathExpression expr = xpath.compile("web-app/context-param[param-name='project']/param-value"); + String str = expr.evaluate(doc, XPathConstants.STRING).toString(); + return StringUtils.trim(str); + } + + protected static String calculateExtraClassPath(Class cls, Path... paths) throws Exception { + List jars = new ArrayList<>(); + jars.addAll(calculateExtraClassPathDefault()); + Module module = cls.getAnnotation(Module.class); + for (String str : module.storeJars()) { + File file = new File(Config.dir_store_jars(), str + ".jar"); + if (file.exists()) { + jars.add(file.getAbsolutePath()); + } + } + for (String str : module.customJars()) { + File file = new File(Config.dir_custom_jars(), str + ".jar"); + if (file.exists()) { + jars.add(file.getAbsolutePath()); + } + } + for (String str : module.dynamicJars()) { + File file = new File(Config.dir_dynamic_jars(), str + ".jar"); + if (file.exists()) { + jars.add(file.getAbsolutePath()); + } + } + for (Path path : paths) { + if (Files.exists(path) && Files.isDirectory(path)) { + try (Stream stream = Files.walk(path, FileVisitOption.FOLLOW_LINKS)) { + stream.filter(Files::isRegularFile) + .filter(p -> p.toAbsolutePath().toString().toLowerCase().endsWith(".jar")) + .forEach(p -> jars.add(p.toAbsolutePath().toString())); + } + } + } + return StringUtils.join(jars, ";"); + } + + private static List calculateExtraClassPathDefault() throws Exception { + List jars = new ArrayList<>(); + IOFileFilter filter = new WildcardFileFilter(x_base_core_project.class.getSimpleName() + "*.jar"); + for (File o : FileUtils.listFiles(Config.dir_store_jars(), filter, null)) { + jars.add(o.getAbsolutePath()); + } + filter = new WildcardFileFilter("openjpa-*.jar"); + filter = FileFilterUtils.or(filter, new WildcardFileFilter("ehcache-*.jar")); + /* 如果不单独导入会导致java.lang.NoClassDefFoundError: org/eclipse/jetty/http/MimeTypes */ + filter = FileFilterUtils.or(filter, new WildcardFileFilter("jetty-all-*.jar")); + filter = FileFilterUtils.or(filter, new WildcardFileFilter("quartz-*.jar")); + if (!com.x.server.console.Main.slf4jOtherImplOn) { + filter = FileFilterUtils.or(filter, new WildcardFileFilter("slf4j-simple-*.jar")); + filter = FileFilterUtils.or(filter, new WildcardFileFilter("jul-to-slf4j-*.jar")); + filter = FileFilterUtils.or(filter, new WildcardFileFilter("log4j-*.jar")); + } + /* jersey从AppClassLoader加载 */ + for (File o : FileUtils.listFiles(Config.dir_commons_ext(), filter, null)) { + jars.add(o.getAbsolutePath()); + } + return jars; + } + public static class CommandObject { private String command; diff --git a/o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/command/ActionUninstall.java b/o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/command/ActionUninstall.java new file mode 100644 index 0000000000000000000000000000000000000000..076d340d87c208c605c910c35ae47ac43f464056 --- /dev/null +++ b/o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/command/ActionUninstall.java @@ -0,0 +1,168 @@ +package com.x.program.center.jaxrs.command; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.InputStream; +import java.net.Socket; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.glassfish.jersey.media.multipart.FormDataContentDisposition; + +import com.hankcs.hanlp.corpus.io.IOUtil; +import com.x.base.core.project.annotation.FieldDescribe; +import com.x.base.core.project.config.Config; +import com.x.base.core.project.config.Nodes; +import com.x.base.core.project.gson.GsonPropertyObject; +import com.x.base.core.project.gson.XGsonBuilder; +import com.x.base.core.project.http.ActionResult; +import com.x.base.core.project.http.EffectivePerson; +import com.x.base.core.project.logger.Logger; +import com.x.base.core.project.logger.LoggerFactory; +import com.x.base.core.project.tools.Crypto; + +public class ActionUninstall extends BaseAction { + private static Logger logger = LoggerFactory.getLogger(CommandAction.class); + public ActionResult execute(HttpServletRequest request, EffectivePerson effectivePerson, String ctl, + String nodeName, String nodePort, String fileName) throws Exception { + ActionResult result = new ActionResult<>(); + Wo wo = null; + String curServer = request.getLocalAddr(); + if(nodeName.equalsIgnoreCase("*")) { + Nodes nodes = Config.nodes(); + logger.info("先其他服务器"); + for (String node : nodes.keySet()){ + //先其他服务器 + if(!node.equalsIgnoreCase(curServer)) { + if(nodes.get(node).getApplication().getEnable() || nodes.get(node).getCenter().getEnable()){ + logger.info("node="+node); + wo = executeCommand( ctl, node , nodes.get(node).nodeAgentPort(), fileName); + } + } + } + + logger.info("后当前服务器"); + for(String node : nodes.keySet()) { + //后当前服务器 + if(node.equalsIgnoreCase(curServer)) { + if(nodes.get(curServer).getApplication().getEnable() || nodes.get(curServer).getCenter().getEnable()){ + logger.info("node="+node); + wo = executeCommand( ctl, node , nodes.get(curServer).nodeAgentPort(), fileName); + } + } + } + }else { + + wo = executeCommand( ctl, nodeName , Integer.parseInt(nodePort), fileName); + } + + result.setData(wo); + return result; + } + + + private Wo executeCommand(String ctl, String nodeName, Integer nodePort, String fileName) { + // TODO Auto-generated method stub + Wo wo = new Wo(); + wo.setNode(nodeName); + wo.setStatus("success"); + try (Socket socket = new Socket(nodeName, nodePort)) { + socket.setKeepAlive(true); + socket.setSoTimeout(5000); + DataOutputStream dos = null; + DataInputStream dis = null; + try { + dos = new DataOutputStream(socket.getOutputStream()); + dis = new DataInputStream(socket.getInputStream()); + + Map commandObject = new HashMap<>(); + commandObject.put("command", "uninstall:"+ ctl); + commandObject.put("credential", Crypto.rsaEncrypt("o2@", Config.publicKey())); + dos.writeUTF(XGsonBuilder.toJson(commandObject)); + dos.flush(); + + dos.writeUTF(fileName); + dos.flush(); + + }finally { + dos.close(); + dis.close(); + socket.close(); + } + } catch (Exception ex) { + wo.setStatus("fail"); + } + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + wo.setTime(df.format(new Date())); + return wo; + } + + + + public static class Wi extends GsonPropertyObject{ + private String ctl; + private String nodeName; + private String nodePort; + + public String getCtl() { + return ctl; + } + public void setCtl(String ctl) { + this.ctl = ctl; + } + public String getNodeName() { + return nodeName; + } + public void setNodeName(String nodeName) { + this.nodeName = nodeName; + } + public String getNodePort() { + return nodePort; + } + public void setNodePort(String nodePort) { + this.nodePort = nodePort; + } + } + + public static class Wo extends GsonPropertyObject { + + @FieldDescribe("执行时间") + private String time; + @FieldDescribe("执行结束") + private String status; + @FieldDescribe("执行服务器") + private String node; + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } + + public String getNode() { + return node; + } + + public void setNode(String node) { + this.node = node; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + } + + +} + diff --git a/o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/command/CommandAction.java b/o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/command/CommandAction.java index ab70fcc8cf3b3ff575de47c4d67156e9bbf343d9..d092c9314fe3a7dc7757408f9c05350b8526b3c2 100644 --- a/o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/command/CommandAction.java +++ b/o2server/x_program_center/src/main/java/com/x/program/center/jaxrs/command/CommandAction.java @@ -94,5 +94,26 @@ public class CommandAction extends StandardJaxrsAction { } asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result)); } + + @JaxrsMethodDescribe(value = "uninstall customWar 包并删除", action = ActionUninstall.class) + @POST + @Path("uninstall") + @Consumes(MediaType.MULTIPART_FORM_DATA) + @Produces(HttpMediaType.APPLICATION_JSON_UTF_8) + public void uninstall(@Suspended final AsyncResponse asyncResponse, @Context HttpServletRequest request, + @JaxrsParameterDescribe("命令名称(customWar)") @FormDataParam("ctl") String ctl, + @JaxrsParameterDescribe("服务器地址(*代表多台应用服务器)") @FormDataParam("nodeName") String nodeName, + @JaxrsParameterDescribe("服务端口") @FormDataParam("nodePort") String nodePort, + @JaxrsParameterDescribe("War名称") @FormDataParam(FILENAME_FIELD) String fileName) { + ActionResult result = new ActionResult<>(); + EffectivePerson effectivePerson = this.effectivePerson(request); + try { + result = new ActionUninstall().execute(request , effectivePerson, ctl, nodeName, nodePort, fileName); + } catch (Exception e) { + logger.error(e, effectivePerson, request, null); + result.error(e); + } + asyncResponse.resume(ResponseFactory.getEntityTagActionResultResponse(request, result)); + } } \ No newline at end of file diff --git a/o2server/x_query_assemble_surface/src/main/java/com/x/query/assemble/surface/jaxrs/view/BaseAction.java b/o2server/x_query_assemble_surface/src/main/java/com/x/query/assemble/surface/jaxrs/view/BaseAction.java index 492a3065b82298824ed4661c6a5a37a32b937e99..0f6328a0642fe033301f16467986838b7b73bcba 100644 --- a/o2server/x_query_assemble_surface/src/main/java/com/x/query/assemble/surface/jaxrs/view/BaseAction.java +++ b/o2server/x_query_assemble_surface/src/main/java/com/x/query/assemble/surface/jaxrs/view/BaseAction.java @@ -1,7 +1,9 @@ package com.x.query.assemble.surface.jaxrs.view; import java.io.ByteArrayOutputStream; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Map; import java.util.Objects; @@ -183,7 +185,8 @@ abstract class BaseAction extends StandardJaxrsAction { i = 0; for (SelectEntry o : plan.selectList) { c = r.createCell(i); - c.setCellValue(Objects.toString(row.find(o.column))); + //c.setCellValue(Objects.toString(row.find(o.column))); + c.setCellValue(objectToString(row.find(o.column))); i++; } } @@ -200,7 +203,26 @@ abstract class BaseAction extends StandardJaxrsAction { return flag; } } - + + protected String objectToString(Object object) { + String str = ""; + if (object instanceof Integer) { + str = object.toString(); + }else if (object instanceof Double) { + str = object.toString(); + }else if (object instanceof Float) { + str = object.toString(); + }else if (object instanceof Boolean) { + str = String.valueOf(object); + } else if (object instanceof Date) { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + str= formatter.format(object); + }else { + str = object.toString(); + } + return str; + } + protected Runtime runtime(EffectivePerson effectivePerson, Business business, View view, List filterList, Map parameter, Integer count, boolean isBundle) throws Exception { Runtime runtime = new Runtime();