All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hreeinfo.commons.embed.server.EmbedServer Maven / Gradle / Ivy

The newest version!
package com.hreeinfo.commons.embed.server;

import com.hreeinfo.commons.embed.server.internal.InternalLifeCycleListener;
import com.hreeinfo.commons.embed.server.internal.InternalNullServer;
import com.hreeinfo.commons.embed.server.internal.InternalOptParsers;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

import java.io.File;
import java.util.*;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 

创建作者:xingxiuyi

*

版权所属:xingxiuyi

*/ public interface EmbedServer { public String getType(); public boolean isRunning(); public void setServerContextLoader(ClassLoader classLoader); /** * 启动服务(前台等待) */ public void start() throws RuntimeException; /** * 启动服务 * * @param daemon 是否以后台方式启动 已后台方式启动时会立即返回 服务在独立线程中执行 */ public void start(boolean daemon) throws RuntimeException; /** * 启动服务 * * @param parentLoader */ public void start(ClassLoader parentLoader, boolean daemon) throws RuntimeException; /** * 启动服务 其中服务运行在独立的守护线程中 *

* 当 daemonThread=true 返回 Future 以便操作服务状态 * * @param parentLoader * @param daemon * @param daemonThread * @return * @throws RuntimeException */ public Future start(ClassLoader parentLoader, boolean daemon, boolean daemonThread) throws RuntimeException; /** * 执行context的reload,重新载入webapp */ public void reload(); /** * 停止服务 停止服务前指定给定操作 */ public void stop(); public static final class Builder { private static final Logger LOG = Logger.getLogger(Builder.class.getName()); private int port = 8080; private String context = ""; private String webapp = ""; private int war = 0; private String workingdir = ""; private String lockfile = ""; private String reloadLockfile = ""; private final List classesdirs = new ArrayList<>(); private final List resourcesdirs = new ArrayList<>(); private final List jars = new ArrayList<>(); private String configfile = ""; private String loglevel = "INFO"; private final Map options = new LinkedHashMap<>(); private final List listeners = new ArrayList<>(); private Consumer config; private final List serverClasspaths = new ArrayList<>(); private final InternalLifeCycleListener innerListener = new InternalLifeCycleListener(); private Builder() { } public Builder port(int port) { this.port = port; return this; } public Builder context(String context) { this.context = context; return this; } public Builder webapp(String webapp) { this.webapp = webapp; this.war = 0; return this; } public Builder webapp(String webapp, boolean war) { this.webapp = webapp; this.war = (war) ? 1 : -1; return this; } public Builder workingdir(String workingdir) { this.workingdir = workingdir; return this; } public Builder configfile(String configfile) { this.configfile = configfile; return this; } public Builder lockfile(String lockfile) { this.lockfile = lockfile; return this; } public Builder reloadLockfile(String reloadLockfile) { this.reloadLockfile = reloadLockfile; return this; } public Builder loglevel(String loglevel) { this.loglevel = loglevel; return this; } public Builder classesdir(String... dirs) { if (dirs != null) this.classesdirs.addAll(Arrays.asList(dirs)); return this; } public Builder resourcedir(String... dirs) { if (dirs != null) this.resourcesdirs.addAll(Arrays.asList(dirs)); return this; } public Builder jar(String... files) { if (files != null) this.jars.addAll(Arrays.asList(files)); return this; } public Builder listener(LifeCycleListener... lns) { if (lns != null) this.listeners.addAll(Arrays.asList(lns)); return this; } public Builder listener(String type, Runnable runnable) { this.innerListener.add(type, runnable); return this; } public Builder option(String name, String value) { if (name != null) this.options.put(name, value); return this; } public Builder options(Map opts) { if (opts != null) this.options.putAll(opts); return this; } public Builder serverClasspath(String... cps) { if (cps != null) this.serverClasspaths.addAll(Arrays.asList(cps)); return this; } private void setFields(BaseEmbedServer embedServer, ClassLoader loader) { if (embedServer == null) return; embedServer.setPort(this.port); embedServer.setContext(this.context); embedServer.setWebapp(this.webapp); embedServer.setWar(this.detectWAR()); embedServer.setWorkingdir(this.workingdir); embedServer.setLockfile(this.lockfile); embedServer.setReloadLockfile(this.reloadLockfile); embedServer.setConfigfile(this.configfile); embedServer.setLoglevel(this.loglevel); embedServer.getClassesdirs().addAll(this.classesdirs); embedServer.getResourcesdirs().addAll(this.resourcesdirs); embedServer.getJars().addAll(this.jars); embedServer.getOptions().putAll(this.options); embedServer.setConfig(this.config); embedServer.getListeners().add(this.innerListener); embedServer.getListeners().addAll(this.listeners); // 此处使用独立的 classloader 以附加 serverClasspaths 定义 embedServer.setServerContextLoader(loader); } /** * 检测当前的webapp是否为war 只有未指定war值时才进行此判断 * * @return */ private boolean detectWAR() { if (this.war > 0) return true; else if (this.war < 0) return false; else { if (StringUtils.isBlank(this.webapp)) return false; File wf = new File(this.webapp); if (wf.exists() && wf.isFile()) return true; else return false; } } /** * 构建目标服务对象 如果类型有误(type无效)则返回默认服务对象(此服务什么也不处理仅打印警告) * * @param type * @return 返回总不为空 * @throws RuntimeException */ public EmbedServer build(String type) throws RuntimeException { ClassLoader loader = BaseEmbedServer.createEmbedServerContextLoader(this.serverClasspaths, null); BaseEmbedServer fes = null; if (StringUtils.isNotBlank(type)) { final ServiceLoader serverLoader = ServiceLoader.load(EmbedServer.class, loader); for (EmbedServer es : serverLoader) { if (es != null && StringUtils.equalsIgnoreCase(type, es.getType()) && (es instanceof BaseEmbedServer)) { fes = (BaseEmbedServer) es; break; } } } if (fes == null) return new InternalNullServer(); this.setFields(fes, loader); return fes; } /** * 按类型构建目标服务对象 无法初始化会抛出异常 类型无效则返回空 * * @param serverType * @param * @return * @throws RuntimeException */ public T build(Class serverType) throws RuntimeException { return build(serverType, null); } /** * 按类型构建目标服务对象 无法初始化会抛出异常 类型无效则返回空 *

* 其中可对目标服务进行额外配置,通过configs进行管理 * * @param serverType * @param config * @param * @return * @throws RuntimeException */ @SuppressWarnings("unchecked") public T build(Class serverType, Consumer config) throws RuntimeException { ClassLoader loader = BaseEmbedServer.createEmbedServerContextLoader(this.serverClasspaths, null); T fes = null; try { fes = serverType.getConstructor().newInstance(); } catch (Throwable e) { LOG.log(Level.SEVERE, "无法构建Server实例 -> " + serverType, e); } if (fes == null) throw new IllegalArgumentException("无法构建 " + serverType + " 实例"); if (config != null) { this.config = (Consumer) config; } if (fes instanceof BaseEmbedServer) this.setFields((BaseEmbedServer) fes, loader); return fes; } public Builder opts(String[] args) { return opts(null, null, args); } /** * 根据命令行参数构建配置对象 *

* 可通过 --argsfile=FILE 从文件读取配置参数(避免命令行过长) * * @param parserConsumer * @param optionSetConsumer * @param args * @return */ public Builder opts(Consumer parserConsumer, Consumer optionSetConsumer, String[] args) { List argList = new ArrayList<>(); if (args == null) args = new String[]{}; String argsfile = null; for (int i = 0; i < args.length; i++) { String a = StringUtils.trimToEmpty(args[i]); if (StringUtils.equalsIgnoreCase(a, "--argsfile")) { if (i < (args.length - 1)) { argsfile = StringUtils.trim(args[i + 1]); break; } } else if (StringUtils.startsWithIgnoreCase(a, "--argsfile=")) { argsfile = StringUtils.trim(StringUtils.substringAfter(a, "=")); break; } } if (StringUtils.isNotBlank(argsfile)) { LOG.log(Level.INFO, "从文件中载入配置 配置文件 " + argsfile); try { File esaf = new File(argsfile); List esas = (esaf.exists() && esaf.isFile()) ? FileUtils.readLines(esaf, "UTF-8") : null; if (esas != null) esas.forEach(s -> { String ps = StringUtils.trim(s); if (StringUtils.isNotBlank(ps)) argList.add(ps); }); } catch (Throwable e) { LOG.log(Level.WARNING, "读取 " + argsfile + " 文件内容发生错误 - " + e.getMessage(), e); } } Collections.addAll(argList, args); OptionParser parser = new OptionParser(); parser.accepts("port").withOptionalArg().ofType(Integer.class); parser.accepts("context").withOptionalArg(); parser.accepts("webapp").withOptionalArg(); parser.accepts("workingdir").withOptionalArg(); parser.accepts("lockfile").withOptionalArg(); parser.accepts("reloadLockfile").withOptionalArg(); parser.accepts("classesdir").withOptionalArg(); parser.accepts("resourcesdir").withOptionalArg(); parser.accepts("jar").withOptionalArg(); parser.accepts("configfile").withOptionalArg(); parser.accepts("loglevel").withOptionalArg(); parser.accepts("classpath").withOptionalArg(); parser.accepts("serverClasspath").withOptionalArg(); parser.accepts("option").withOptionalArg(); parser.accepts("argsfile").withOptionalArg(); if (parserConsumer != null) parserConsumer.accept(parser); OptionSet osts = parser.parse(argList.toArray(new String[]{})); if (osts == null) return this; // 先读取 esargsfile 参数 this.port = InternalOptParsers.optInteger(osts, "port", 8080); this.context = InternalOptParsers.optString(osts, "context", ""); this.webapp = InternalOptParsers.optString(osts, "webapp", ""); this.workingdir = InternalOptParsers.optString(osts, "workingdir", ""); this.lockfile = InternalOptParsers.optString(osts, "lockfile", ""); this.reloadLockfile = InternalOptParsers.optString(osts, "reloadLockfile", ""); InternalOptParsers.opt(osts, "classesdir", o -> InternalOptParsers.optAppendPathValues(o, this.classesdirs)); InternalOptParsers.opt(osts, "resourcesdir", o -> InternalOptParsers.optAppendPathValues(o, this.resourcesdirs)); InternalOptParsers.opt(osts, "jar", o -> InternalOptParsers.optAppendPathValues(o, this.jars)); this.configfile = InternalOptParsers.optString(osts, "configfile", ""); this.loglevel = StringUtils.upperCase(InternalOptParsers.optString(osts, "loglevel", "INFO")); InternalOptParsers.opt(osts, "classpath", o -> InternalOptParsers.optAppendPathValues(o, this.serverClasspaths)); InternalOptParsers.opt(osts, "serverClasspath", o -> InternalOptParsers.optAppendPathValues(o, this.serverClasspaths)); InternalOptParsers.opt(osts, "option", o -> InternalOptParsers.optAppendMaValues(o, this.options)); if (optionSetConsumer != null) optionSetConsumer.accept(osts); return this; } public static Builder builder() { return new Builder(); } @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) .append("port", port) .append("context", context) .append("webapp", webapp) .append("war", war) .append("workingdir", workingdir) .append("lockfile", lockfile) .append("classesdirs", classesdirs) .append("resourcesdirs", resourcesdirs) .append("jars", jars) .append("configfile", configfile) .append("loglevel", loglevel) .append("options", options) .append("listeners", listeners) .append("config", config) .append("serverClasspaths", serverClasspaths) .append("innerListener", innerListener) .toString(); } } public interface LifeCycleListener extends EventListener { public void onStarting(EmbedServer server); public void onStarted(EmbedServer server); public void onFailure(EmbedServer server, Throwable cause); public void onStopping(EmbedServer server); public void onStopped(EmbedServer server); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy