From 8a0e477f693fea71042df3bb5bca6df15d7cb8b1 Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 21 Sep 2023 21:14:00 +0800 Subject: [PATCH] add session --- .../hutool/core/cache/SimpleCache.java | 11 + .../hutool/core/spi/ListServiceLoader.java | 39 +-- .../hutool/core/spi/MapServiceLoader.java | 21 +- .../hutool/core/spi/ServiceLoader.java | 27 +- .../hutool/extra/pinyin/PinyinUtil.java | 10 + .../pinyin/engine/PinyinEngineFactory.java | 21 ++ .../HoubbEngine.java} | 8 +- .../{houbbpinyin => houbb}/package-info.java | 2 +- .../dromara/hutool/extra/ssh/Connector.java | 15 +- .../extra/ssh/JschRuntimeException.java | 48 --- .../org/dromara/hutool/extra/ssh/Session.java | 25 ++ .../hutool/extra/ssh/SshException.java | 85 ++++++ .../ganymed/GanymedSession.java} | 108 ++++--- .../ssh/{ => engine/jsch}/ChannelType.java | 2 +- .../extra/ssh/engine/jsch/JschSession.java | 283 ++++++++++++++++++ .../{ => engine/jsch}/JschSessionPool.java | 2 +- .../extra/ssh/{ => engine/jsch}/JschUtil.java | 44 +-- .../extra/ssh/{ => engine/jsch}/Sftp.java | 29 +- .../extra/ssh/engine/jsch/package-info.java | 2 +- .../extra/ssh/{ => engine/sshj}/SshjSftp.java | 2 +- .../hutool/extra/tokenizer/TokenizerUtil.java | 10 + .../engine/TokenizerEngineFactory.java | 25 +- ...ra.hutool.extra.pinyin.engine.PinyinEngine | 2 +- .../org/dromara/hutool/extra/ftp/FtpTest.java | 2 +- .../hutool/extra/pinyin/Bopomofo4jTest.java | 4 +- .../hutool/extra/pinyin/HoubbPinyinTest.java | 4 +- .../hutool/extra/pinyin/JpinyinTest.java | 4 +- .../hutool/extra/pinyin/Pinyin4jTest.java | 4 +- .../hutool/extra/pinyin/TinyPinyinTest.java | 4 +- .../hutool/extra/ssh/JschUtilTest.java | 4 +- .../dromara/hutool/extra/ssh/SftpTest.java | 1 + .../extra/tokenizer/TokenizerUtilTest.java | 24 +- .../org/dromara/hutool/http/HttpUtil.java | 31 +- .../client/engine/ClientEngineFactory.java | 21 ++ .../http/client/HttpClient4EngineTest.java | 4 +- .../http/client/HttpClient5EngineTest.java | 4 +- .../hutool/http/client/Issue3240Test.java | 7 +- .../hutool/http/client/IssueI7ZRJUTest.java | 2 +- .../hutool/http/client/JdkEngineTest.java | 4 +- 39 files changed, 707 insertions(+), 238 deletions(-) rename hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/{houbbpinyin/HoubbPinyinEngine.java => houbb/HoubbEngine.java} (89%) rename hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/{houbbpinyin => houbb}/package-info.java (93%) delete mode 100644 hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/JschRuntimeException.java create mode 100644 hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/Session.java create mode 100644 hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/SshException.java rename hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/{GanymedUtil.java => engine/ganymed/GanymedSession.java} (52%) rename hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/{ => engine/jsch}/ChannelType.java (96%) create mode 100644 hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/JschSession.java rename hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/{ => engine/jsch}/JschSessionPool.java (98%) rename hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/{ => engine/jsch}/JschUtil.java (93%) rename hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/{ => engine/jsch}/Sftp.java (97%) rename hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/{ => engine/sshj}/SshjSftp.java (99%) diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/cache/SimpleCache.java b/hutool-core/src/main/java/org/dromara/hutool/core/cache/SimpleCache.java index 14296a2d7..addc9fc91 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/cache/SimpleCache.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/cache/SimpleCache.java @@ -21,6 +21,7 @@ import org.dromara.hutool.core.map.WeakConcurrentMap; import java.io.Serializable; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.locks.Lock; @@ -28,6 +29,7 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Predicate; +import java.util.stream.Collectors; /** * 简单缓存,无超时实现,默认使用{@link WeakConcurrentMap}实现缓存自动清理 @@ -212,4 +214,13 @@ public class SimpleCache implements Iterable>, Serializabl } }); } + + /** + * 获取所有键 + * + * @return 所有键 + */ + public List keys(){ + return this.rawMap.keySet().stream().map(Mutable::get).collect(Collectors.toList()); + } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/spi/ListServiceLoader.java b/hutool-core/src/main/java/org/dromara/hutool/core/spi/ListServiceLoader.java index b07282c4c..a7e5495f0 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/spi/ListServiceLoader.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/spi/ListServiceLoader.java @@ -14,6 +14,7 @@ package org.dromara.hutool.core.spi; import org.dromara.hutool.core.cache.SimpleCache; import org.dromara.hutool.core.classloader.ClassLoaderUtil; +import org.dromara.hutool.core.collection.ListUtil; import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.resource.MultiResource; import org.dromara.hutool.core.io.resource.Resource; @@ -24,10 +25,7 @@ import org.dromara.hutool.core.text.StrUtil; import java.io.BufferedReader; import java.io.IOException; import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; +import java.util.*; /** * 列表类型的服务加载器,用于替换JDK提供的{@link java.util.ServiceLoader}
@@ -136,6 +134,11 @@ public class ListServiceLoader extends AbsServiceLoader { return this.serviceNames.size(); } + @Override + public List getServiceNames(){ + return ListUtil.view(this.serviceNames); + } + /** * 获取指定服务的实现类 * @@ -148,7 +151,12 @@ public class ListServiceLoader extends AbsServiceLoader { return null; } - return ClassLoaderUtil.loadClass(serviceClassName); + return getServiceClass(serviceClassName); + } + + @Override + public Class getServiceClass(final String serviceName) { + return ClassLoaderUtil.loadClass(serviceName); } /** @@ -162,7 +170,12 @@ public class ListServiceLoader extends AbsServiceLoader { if (null == serviceClassName) { return null; } - return getServiceByName(serviceClassName); + return getService(serviceClassName); + } + + @Override + public S getService(final String serviceName) { + return this.serviceCache.get(serviceName, () -> createService(serviceName)); } @Override @@ -177,7 +190,7 @@ public class ListServiceLoader extends AbsServiceLoader { @Override public S next() { - return getServiceByName(nameIter.next()); + return getService(nameIter.next()); } }; } @@ -223,7 +236,7 @@ public class ListServiceLoader extends AbsServiceLoader { line = line.substring(0, ci); } line = StrUtil.trim(line); - if (line.length() > 0) { + if (!line.isEmpty()) { // 检查行 checkLine(resource, lineNo, line); // 不覆盖模式 @@ -272,16 +285,6 @@ public class ListServiceLoader extends AbsServiceLoader { throw new SPIException(this.serviceClass + ":" + resource.getUrl() + ":" + lineNo + ": " + msg); } - /** - * 获取指定class名对应的服务,使用缓存,多次调用只返回相同的服务对象 - * - * @param serviceClassName 服务名称 - * @return 服务对象 - */ - private S getServiceByName(final String serviceClassName) { - return this.serviceCache.get(serviceClassName, () -> createService(serviceClassName)); - } - /** * 创建服务,无缓存 * diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/spi/MapServiceLoader.java b/hutool-core/src/main/java/org/dromara/hutool/core/spi/MapServiceLoader.java index f9106efa9..10ee1ebfe 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/spi/MapServiceLoader.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/spi/MapServiceLoader.java @@ -14,6 +14,7 @@ package org.dromara.hutool.core.spi; import org.dromara.hutool.core.cache.SimpleCache; import org.dromara.hutool.core.classloader.ClassLoaderUtil; +import org.dromara.hutool.core.collection.ListUtil; import org.dromara.hutool.core.io.resource.ResourceUtil; import org.dromara.hutool.core.reflect.ConstructorUtil; import org.dromara.hutool.core.text.StrUtil; @@ -21,6 +22,7 @@ import org.dromara.hutool.core.text.StrUtil; import java.nio.charset.Charset; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Properties; /** @@ -128,12 +130,12 @@ public class MapServiceLoader extends AbsServiceLoader { return this.serviceProperties.size(); } - /** - * 获取指定服务的实现类 - * - * @param serviceName 服务名称 - * @return 服务名称对应的实现类 - */ + @Override + public List getServiceNames() { + return ListUtil.view(this.serviceCache.keys()); + } + + @Override public Class getServiceClass(final String serviceName) { final String serviceClassName = this.serviceProperties.getProperty(serviceName); if (StrUtil.isBlank(serviceClassName)) { @@ -143,12 +145,7 @@ public class MapServiceLoader extends AbsServiceLoader { return ClassLoaderUtil.loadClass(serviceClassName); } - /** - * 获取指定名称对应的服务,使用缓存,多次调用只返回相同的服务对象 - * - * @param serviceName 服务名称 - * @return 服务对象 - */ + @Override public S getService(final String serviceName) { return this.serviceCache.get(serviceName, () -> createService(serviceName)); } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/spi/ServiceLoader.java b/hutool-core/src/main/java/org/dromara/hutool/core/spi/ServiceLoader.java index d4ae8a376..9ed71e499 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/spi/ServiceLoader.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/spi/ServiceLoader.java @@ -12,6 +12,8 @@ package org.dromara.hutool.core.spi; +import java.util.List; + /** * SPI服务加载接口
* 用户实现此接口用于制定不同的服务加载方式 @@ -19,7 +21,7 @@ package org.dromara.hutool.core.spi; * @param 服务对象类型 * @author looly */ -public interface ServiceLoader extends Iterable{ +public interface ServiceLoader extends Iterable { /** * 加载服务 @@ -32,4 +34,27 @@ public interface ServiceLoader extends Iterable{ * @return 总数 */ int size(); + + /** + * 获取服务名称列表 + * + * @return 服务名称列表 + */ + List getServiceNames(); + + /** + * 获取指定服务的实现类 + * + * @param serviceName 服务名称 + * @return 服务名称对应的实现类 + */ + Class getServiceClass(String serviceName); + + /** + * 获取指定名称对应的服务 + * + * @param serviceName 服务名称 + * @return 服务对象 + */ + S getService(String serviceName); } diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/PinyinUtil.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/PinyinUtil.java index 20e289245..6bfa00986 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/PinyinUtil.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/PinyinUtil.java @@ -25,6 +25,16 @@ import org.dromara.hutool.extra.pinyin.engine.PinyinEngineFactory; */ public class PinyinUtil { + /** + * 创建拼音引擎 + * + * @param engineName 引擎名称 + * @return {@link PinyinEngine} + */ + public static PinyinEngine createEngine(final String engineName) { + return PinyinEngineFactory.createEngine(engineName); + } + /** * 获得全局单例的拼音引擎 * diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/PinyinEngineFactory.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/PinyinEngineFactory.java index 3a1099c5a..ee95c68fa 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/PinyinEngineFactory.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/PinyinEngineFactory.java @@ -13,6 +13,7 @@ package org.dromara.hutool.extra.pinyin.engine; import org.dromara.hutool.core.lang.Singleton; +import org.dromara.hutool.core.spi.ServiceLoader; import org.dromara.hutool.core.spi.SpiUtil; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.extra.pinyin.PinyinException; @@ -47,6 +48,26 @@ public class PinyinEngineFactory { return doCreateEngine(); } + /** + * 创建自定义引擎 + * + * @param engineName 引擎名称,忽略大小写,如`Bopomofo4j`、`Houbb`、`JPinyin`、`Pinyin4j`、`TinyPinyin` + * @return 引擎 + * @throws PinyinException 无对应名称的引擎 + */ + public static PinyinEngine createEngine(String engineName) throws PinyinException { + if (!StrUtil.endWithIgnoreCase(engineName, "Engine")) { + engineName = engineName + "Engine"; + } + final ServiceLoader list = SpiUtil.loadList(PinyinEngine.class); + for (final String serviceName : list.getServiceNames()) { + if (StrUtil.endWithIgnoreCase(serviceName, engineName)) { + return list.getService(serviceName); + } + } + throw new PinyinException("No such engine named: " + engineName); + } + /** * 根据用户引入的拼音引擎jar,自动创建对应的拼音引擎对象
* 推荐创建的引擎单例使用,此方法每次调用会返回新的引擎 diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/houbbpinyin/HoubbPinyinEngine.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/houbb/HoubbEngine.java similarity index 89% rename from hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/houbbpinyin/HoubbPinyinEngine.java rename to hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/houbb/HoubbEngine.java index 947659e34..70e36ae8d 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/houbbpinyin/HoubbPinyinEngine.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/houbb/HoubbEngine.java @@ -10,7 +10,7 @@ * See the Mulan PSL v2 for more details. */ -package org.dromara.hutool.extra.pinyin.engine.houbbpinyin; +package org.dromara.hutool.extra.pinyin.engine.houbb; import org.dromara.hutool.extra.pinyin.engine.PinyinEngine; import com.github.houbb.pinyin.constant.enums.PinyinStyleEnum; @@ -35,7 +35,7 @@ import com.github.houbb.pinyin.util.PinyinHelper; * * @author looly */ -public class HoubbPinyinEngine implements PinyinEngine { +public class HoubbEngine implements PinyinEngine { /** * 汉字拼音输出的格式 @@ -45,7 +45,7 @@ public class HoubbPinyinEngine implements PinyinEngine { /** * 构造 */ - public HoubbPinyinEngine() { + public HoubbEngine() { this(null); } @@ -54,7 +54,7 @@ public class HoubbPinyinEngine implements PinyinEngine { * * @param format 格式 */ - public HoubbPinyinEngine(final PinyinStyleEnum format) { + public HoubbEngine(final PinyinStyleEnum format) { init(format); } diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/houbbpinyin/package-info.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/houbb/package-info.java similarity index 93% rename from hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/houbbpinyin/package-info.java rename to hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/houbb/package-info.java index e18a8e74a..3ddde54b9 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/houbbpinyin/package-info.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/pinyin/engine/houbb/package-info.java @@ -29,4 +29,4 @@ * * @author looly */ -package org.dromara.hutool.extra.pinyin.engine.houbbpinyin; +package org.dromara.hutool.extra.pinyin.engine.houbb; diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/Connector.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/Connector.java index 010c8b23e..d7b712f3d 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/Connector.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/Connector.java @@ -15,13 +15,12 @@ package org.dromara.hutool.extra.ssh; /** * 连接者对象,提供一些连接的基本信息 * - * @author Luxiaolei - * + * @author looly */ public class Connector { private String host; private int port; - private String user; + private String user = "root"; private String password; private String group; @@ -34,9 +33,9 @@ public class Connector { /** * 构造 * - * @param user 用户名 + * @param user 用户名 * @param password 密码 - * @param group 组 + * @param group 组 */ public Connector(final String user, final String password, final String group) { this.user = user; @@ -47,9 +46,9 @@ public class Connector { /** * 构造 * - * @param host 主机名 - * @param port 端口 - * @param user 用户名 + * @param host 主机名 + * @param port 端口 + * @param user 用户名 * @param password 密码 */ public Connector(final String host, final int port, final String user, final String password) { diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/JschRuntimeException.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/JschRuntimeException.java deleted file mode 100644 index 8bbb08696..000000000 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/JschRuntimeException.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2023 looly(loolly@aliyun.com) - * Hutool is licensed under Mulan PSL v2. - * You can use this software according to the terms and conditions of the Mulan PSL v2. - * You may obtain a copy of Mulan PSL v2 at: - * https://license.coscl.org.cn/MulanPSL2 - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, - * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * See the Mulan PSL v2 for more details. - */ - -package org.dromara.hutool.extra.ssh; - -import org.dromara.hutool.core.exception.ExceptionUtil; -import org.dromara.hutool.core.text.StrUtil; - -/** - * Jsch异常 - * @author xiaoleilu - */ -public class JschRuntimeException extends RuntimeException{ - private static final long serialVersionUID = 8247610319171014183L; - - public JschRuntimeException(final Throwable e) { - super(ExceptionUtil.getMessage(e), e); - } - - public JschRuntimeException(final String message) { - super(message); - } - - public JschRuntimeException(final String messageTemplate, final Object... params) { - super(StrUtil.format(messageTemplate, params)); - } - - public JschRuntimeException(final String message, final Throwable throwable) { - super(message, throwable); - } - - public JschRuntimeException(final String message, final Throwable throwable, final boolean enableSuppression, final boolean writableStackTrace) { - super(message, throwable, enableSuppression, writableStackTrace); - } - - public JschRuntimeException(final Throwable throwable, final String messageTemplate, final Object... params) { - super(StrUtil.format(messageTemplate, params), throwable); - } -} diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/Session.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/Session.java new file mode 100644 index 000000000..9f4380817 --- /dev/null +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/Session.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * https://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.extra.ssh; + +import org.dromara.hutool.core.func.Wrapper; + +import java.io.Closeable; + +/** + * SSH Session抽象 + * + * @author looly + */ +public interface Session extends Wrapper, Closeable { +} diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/SshException.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/SshException.java new file mode 100644 index 000000000..285cf0e01 --- /dev/null +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/SshException.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * https://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.extra.ssh; + +import org.dromara.hutool.core.exception.HutoolException; + +/** + * SSH异常 + * + * @author xiaoleilu + */ +public class SshException extends HutoolException { + private static final long serialVersionUID = 1L; + + /** + * 构造 + * + * @param e 异常 + */ + public SshException(final Throwable e) { + super(e); + } + + /** + * 构造 + * + * @param message 消息 + */ + public SshException(final String message) { + super(message); + } + + /** + * 构造 + * + * @param messageTemplate 消息模板 + * @param params 参数 + */ + public SshException(final String messageTemplate, final Object... params) { + super(messageTemplate, params); + } + + /** + * 构造 + * + * @param message 消息 + * @param cause 被包装的子异常 + */ + public SshException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * 构造 + * + * @param message 消息 + * @param cause 被包装的子异常 + * @param enableSuppression 是否启用抑制 + * @param writableStackTrace 堆栈跟踪是否应该是可写的 + */ + public SshException(final String message, final Throwable cause, final boolean enableSuppression, final boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + /** + * 构造 + * + * @param cause 被包装的子异常 + * @param messageTemplate 消息模板 + * @param params 参数 + */ + public SshException(final Throwable cause, final String messageTemplate, final Object... params) { + super(cause, messageTemplate, params); + } +} diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/GanymedUtil.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/ganymed/GanymedSession.java similarity index 52% rename from hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/GanymedUtil.java rename to hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/ganymed/GanymedSession.java index 6fcb57755..69c523bb4 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/GanymedUtil.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/ganymed/GanymedSession.java @@ -10,65 +10,55 @@ * See the Mulan PSL v2 for more details. */ -package org.dromara.hutool.extra.ssh; +package org.dromara.hutool.extra.ssh.engine.ganymed; import ch.ethz.ssh2.Connection; -import ch.ethz.ssh2.Session; import ch.ethz.ssh2.StreamGobbler; import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.IoUtil; -import org.dromara.hutool.core.text.StrUtil; +import org.dromara.hutool.extra.ssh.Connector; +import org.dromara.hutool.extra.ssh.Session; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; /** - * Ganymed-SSH2封装,见:https://www.ganymed.ethz.ch/ssh2/ + * {@link ch.ethz.ssh2.Session}包装 * * @author looly - * @since 5.5.3 */ -public class GanymedUtil { +public class GanymedSession implements Session { + + private final ch.ethz.ssh2.Session raw; /** - * 连接到服务器 + * 构造 * - * @param sshHost 主机 - * @param sshPort 端口 - * @return {@link Connection} + * @param connector {@link Connector},保存连接和验证信息等 */ - public static Connection connect(final String sshHost, final int sshPort) { - final Connection conn = new Connection(sshHost, sshPort); - try { - conn.connect(); - } catch (final IOException e) { - throw new IORuntimeException(e); - } - return conn; + public GanymedSession(final Connector connector) { + this(openSession(connector)); } /** - * 打开远程会话 + * 构造 * - * @param sshHost 主机 - * @param sshPort 端口 - * @param sshUser 用户名,如果为null,默认root - * @param sshPass 密码 - * @return {@link Session} + * @param raw {@link ch.ethz.ssh2.Session} */ - public static Session openSession(final String sshHost, final int sshPort, String sshUser, final String sshPass) { - // 默认root用户 - if (StrUtil.isEmpty(sshUser)) { - sshUser = "root"; - } + public GanymedSession(final ch.ethz.ssh2.Session raw) { + this.raw = raw; + } - final Connection connect = connect(sshHost, sshPort); - try { - connect.authenticateWithPassword(sshUser, sshPass); - return connect.openSession(); - } catch (final IOException e) { - throw new IORuntimeException(e); + @Override + public ch.ethz.ssh2.Session getRaw() { + return raw; + } + + @Override + public void close() throws IOException { + if (raw != null) { + raw.close(); } } @@ -78,24 +68,21 @@ public class GanymedUtil { * 此方法单次发送一个命令到服务端,不读取环境变量,执行结束后自动关闭Session,不会产生阻塞。 *

* - * @param session Session会话 * @param cmd 命令 * @param charset 发送和读取内容的编码 * @param errStream 错误信息输出到的位置 * @return 执行返回结果 */ - public static String exec(final Session session, final String cmd, final Charset charset, final OutputStream errStream) { + public String exec(final String cmd, final Charset charset, final OutputStream errStream) { final String result; try { - session.execCommand(cmd, charset.name()); - result = IoUtil.read(new StreamGobbler(session.getStdout()), charset); + this.raw.execCommand(cmd, charset.name()); + result = IoUtil.read(new StreamGobbler(this.raw.getStdout()), charset); // 错误输出 - IoUtil.copy(new StreamGobbler(session.getStderr()), errStream); + IoUtil.copy(new StreamGobbler(this.raw.getStderr()), errStream); } catch (final IOException e) { throw new IORuntimeException(e); - } finally { - close(session); } return result; } @@ -106,39 +93,50 @@ public class GanymedUtil { * 此方法单次发送一个命令到服务端,自动读取环境变量,执行结束后自动关闭Session,可能产生阻塞。 *

* - * @param session Session会话 * @param cmd 命令 * @param charset 发送和读取内容的编码 * @param errStream 错误信息输出到的位置 * @return 执行返回结果 */ - public static String execByShell(final Session session, final String cmd, final Charset charset, final OutputStream errStream) { + public String execByShell(final String cmd, final Charset charset, final OutputStream errStream) { final String result; try { - session.requestDumbPTY(); - IoUtil.write(session.getStdin(), charset, true, cmd); + this.raw.requestDumbPTY(); + IoUtil.write(this.raw.getStdin(), charset, true, cmd); - result = IoUtil.read(new StreamGobbler(session.getStdout()), charset); + result = IoUtil.read(new StreamGobbler(this.raw.getStdout()), charset); if(null != errStream){ // 错误输出 - IoUtil.copy(new StreamGobbler(session.getStderr()), errStream); + IoUtil.copy(new StreamGobbler(this.raw.getStderr()), errStream); } } catch (final IOException e) { throw new IORuntimeException(e); - } finally { - close(session); } return result; } /** - * 关闭会话 + * 初始化并打开新的Session * - * @param session 会话通道 + * @param connector {@link Connector},保存连接和验证信息等 + * @return {@link ch.ethz.ssh2.Session} */ - public static void close(final Session session) { - if (session != null) { - session.close(); + private static ch.ethz.ssh2.Session openSession(final Connector connector) { + + // 建立连接 + final Connection conn = new Connection(connector.getHost(), connector.getPort()); + try { + conn.connect(); + } catch (final IOException e) { + throw new IORuntimeException(e); + } + + // 打开会话 + try { + conn.authenticateWithPassword(connector.getUser(), connector.getPassword()); + return conn.openSession(); + } catch (final IOException e) { + throw new IORuntimeException(e); } } } diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/ChannelType.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/ChannelType.java similarity index 96% rename from hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/ChannelType.java rename to hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/ChannelType.java index 673ee03d6..e35545d97 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/ChannelType.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/ChannelType.java @@ -10,7 +10,7 @@ * See the Mulan PSL v2 for more details. */ -package org.dromara.hutool.extra.ssh; +package org.dromara.hutool.extra.ssh.engine.jsch; /** * Jsch支持的Channel类型 diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/JschSession.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/JschSession.java new file mode 100644 index 000000000..ab907a169 --- /dev/null +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/JschSession.java @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2023 looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * https://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.extra.ssh.engine.jsch; + +import com.jcraft.jsch.*; +import org.dromara.hutool.core.io.IORuntimeException; +import org.dromara.hutool.core.io.IoUtil; +import org.dromara.hutool.core.util.ByteUtil; +import org.dromara.hutool.core.util.CharsetUtil; +import org.dromara.hutool.extra.ssh.Connector; +import org.dromara.hutool.extra.ssh.Session; +import org.dromara.hutool.extra.ssh.SshException; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; + +/** + * Jsch Session封装 + */ +public class JschSession implements Session { + + private final com.jcraft.jsch.Session raw; + + /** + * 构造 + * + * @param connector {@link Connector},保存连接和验证信息等 + */ + public JschSession(final Connector connector) { + this(openSession(connector)); + } + + /** + * 构造 + * + * @param raw {@link com.jcraft.jsch.Session} + */ + public JschSession(final com.jcraft.jsch.Session raw) { + this.raw = raw; + } + + @Override + public Object getRaw() { + return raw; + } + + @Override + public void close() throws IOException { + if (raw != null && raw.isConnected()) { + raw.disconnect(); + } + } + + /** + * 绑定端口到本地。 一个会话可绑定多个端口 + * + * @param remoteHost 远程主机 + * @param remotePort 远程端口 + * @param localHost 本地主机 + * @param localPort 本地端口 + * @return 成功与否 + * @throws SshException 端口绑定失败异常 + */ + public boolean bindPort(final String remoteHost, final int remotePort, final String localHost, final int localPort) throws SshException { + if (this.raw != null && this.raw.isConnected()) { + try { + this.raw.setPortForwardingL(localHost, localPort, remoteHost, remotePort); + } catch (final JSchException e) { + throw new SshException(e, "From [{}:{}] mapping to [{}:{}] error!", remoteHost, remotePort, localHost, localPort); + } + return true; + } + return false; + } + + /** + * 绑定ssh服务端的serverPort端口, 到host主机的port端口上.
+ * 即数据从ssh服务端的serverPort端口, 流经ssh客户端, 达到host:port上. + * + * @param bindPort ssh服务端上要被绑定的端口 + * @param host 转发到的host + * @param port host上的端口 + * @return 成功与否 + * @throws SshException 端口绑定失败异常 + */ + public boolean bindRemotePort(final int bindPort, final String host, final int port) throws SshException { + if (this.raw != null && this.raw.isConnected()) { + try { + this.raw.setPortForwardingR(bindPort, host, port); + } catch (final JSchException e) { + throw new SshException(e, "From [{}] mapping to [{}] error!", bindPort, port); + } + return true; + } + return false; + } + + /** + * 解除端口映射 + * + * @param localPort 需要解除的本地端口 + */ + public void unBindPort(final int localPort) { + try { + this.raw.delPortForwardingL(localPort); + } catch (final JSchException e) { + throw new SshException(e); + } + } + + /** + * 创建Channel连接 + * + * @param channelType 通道类型,可以是shell或sftp等,见{@link ChannelType} + * @return {@link Channel} + */ + public Channel createChannel(final ChannelType channelType) { + final Channel channel; + try { + if (!this.raw.isConnected()) { + this.raw.connect(); + } + channel = this.raw.openChannel(channelType.getValue()); + } catch (final JSchException e) { + throw new SshException(e); + } + return channel; + } + + /** + * 打开Shell连接 + * + * @return {@link ChannelShell} + */ + public ChannelShell openShell() { + return (ChannelShell) openChannel(ChannelType.SHELL); + } + + /** + * 打开Channel连接 + * + * @param channelType 通道类型,可以是shell或sftp等,见{@link ChannelType} + * @return {@link Channel} + */ + public Channel openChannel(final ChannelType channelType) { + return openChannel(channelType, 0); + } + + /** + * 打开Channel连接 + * + * @param channelType 通道类型,可以是shell或sftp等,见{@link ChannelType} + * @param timeout 连接超时时长,单位毫秒 + * @return {@link Channel} + */ + public Channel openChannel(final ChannelType channelType, final int timeout) { + final Channel channel = createChannel(channelType); + try { + channel.connect(Math.max(timeout, 0)); + } catch (final JSchException e) { + throw new SshException(e); + } + return channel; + } + + /** + * 执行Shell命令 + * + * @param cmd 命令 + * @param charset 发送和读取内容的编码 + * @return {@link ChannelExec} + */ + public String exec(final String cmd, final Charset charset) { + return exec(cmd, charset, System.err); + } + + /** + * 执行Shell命令(使用EXEC方式) + *

+ * 此方法单次发送一个命令到服务端,不读取环境变量,执行结束后自动关闭channel,不会产生阻塞。 + *

+ * + * @param cmd 命令 + * @param charset 发送和读取内容的编码 + * @param errStream 错误信息输出到的位置 + * @return 执行结果内容 + * @since 4.3.1 + */ + public String exec(final String cmd, Charset charset, final OutputStream errStream) { + if (null == charset) { + charset = CharsetUtil.UTF_8; + } + final ChannelExec channel = (ChannelExec) createChannel(ChannelType.EXEC); + channel.setCommand(ByteUtil.toBytes(cmd, charset)); + channel.setInputStream(null); + channel.setErrStream(errStream); + InputStream in = null; + try { + channel.connect(); + in = channel.getInputStream(); + return IoUtil.read(in, charset); + } catch (final IOException e) { + throw new IORuntimeException(e); + } catch (final JSchException e) { + throw new SshException(e); + } finally { + IoUtil.closeQuietly(in); + if (channel.isConnected()) { + channel.disconnect(); + } + } + } + + /** + * 执行Shell命令 + *

+ * 此方法单次发送一个命令到服务端,自动读取环境变量,执行结束后自动关闭channel,不会产生阻塞。 + *

+ * + * @param cmd 命令 + * @param charset 发送和读取内容的编码 + * @return {@link ChannelExec} + * @since 5.2.5 + */ + public static String execByShell(final String cmd, final Charset charset) { + final ChannelShell shell = openShell(); + // 开始连接 + shell.setPty(true); + OutputStream out = null; + InputStream in = null; + try { + out = shell.getOutputStream(); + in = shell.getInputStream(); + + out.write(ByteUtil.toBytes(cmd, charset)); + out.flush(); + + return IoUtil.read(in, charset); + } catch (final IOException e) { + throw new IORuntimeException(e); + } finally { + IoUtil.closeQuietly(out); + IoUtil.closeQuietly(in); + if (shell.isConnected()) { + shell.disconnect(); + } + } + } + + /** + * 创建{@link com.jcraft.jsch.Session} + * + * @param connector {@link Connector},保存连接和验证信息等 + * @return {@link com.jcraft.jsch.Session} + */ + private static com.jcraft.jsch.Session openSession(final Connector connector) { + final JSch jsch = new JSch(); + final com.jcraft.jsch.Session session; + try { + session = jsch.getSession(connector.getUser(), connector.getHost(), connector.getPort()); + } catch (final JSchException e) { + throw new SshException(e); + } + + session.setPassword(connector.getPassword()); + // 设置第一次登录的时候提示,可选值:(ask | yes | no) + session.setConfig("StrictHostKeyChecking", "no"); + + return session; + } +} diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/JschSessionPool.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/JschSessionPool.java similarity index 98% rename from hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/JschSessionPool.java rename to hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/JschSessionPool.java index 7ffd6b6c5..92a4df792 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/JschSessionPool.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/JschSessionPool.java @@ -10,7 +10,7 @@ * See the Mulan PSL v2 for more details. */ -package org.dromara.hutool.extra.ssh; +package org.dromara.hutool.extra.ssh.engine.jsch; import org.dromara.hutool.core.cache.SimpleCache; import org.dromara.hutool.core.text.StrUtil; diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/JschUtil.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/JschUtil.java similarity index 93% rename from hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/JschUtil.java rename to hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/JschUtil.java index 446394538..6f3aed9db 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/JschUtil.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/JschUtil.java @@ -10,8 +10,9 @@ * See the Mulan PSL v2 for more details. */ -package org.dromara.hutool.extra.ssh; +package org.dromara.hutool.extra.ssh.engine.jsch; +import com.jcraft.jsch.*; import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.lang.Assert; @@ -19,7 +20,8 @@ import org.dromara.hutool.core.net.LocalPortGenerator; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.util.ByteUtil; import org.dromara.hutool.core.util.CharsetUtil; -import com.jcraft.jsch.*; +import org.dromara.hutool.extra.ssh.Connector; +import org.dromara.hutool.extra.ssh.SshException; import java.io.IOException; import java.io.InputStream; @@ -112,7 +114,7 @@ public class JschUtil { session.setTimeout(timeout); session.connect(timeout); } catch (final JSchException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } return session; } @@ -132,7 +134,7 @@ public class JschUtil { try { session.connect(); } catch (final JSchException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } return session; } @@ -154,7 +156,7 @@ public class JschUtil { session.setTimeout(timeout); session.connect(timeout); } catch (final JSchException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } return session; } @@ -198,7 +200,7 @@ public class JschUtil { try { jsch.addIdentity(privateKeyPath, passphrase); } catch (final JSchException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } return createSession(jsch, sshHost, sshPort, sshUser); @@ -231,7 +233,7 @@ public class JschUtil { try { session = jsch.getSession(sshUser, sshHost, sshPort); } catch (final JSchException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } // 设置第一次登录的时候提示,可选值:(ask | yes | no) @@ -248,9 +250,9 @@ public class JschUtil { * @param remotePort 远程端口 * @param localPort 本地端口 * @return 成功与否 - * @throws JschRuntimeException 端口绑定失败异常 + * @throws SshException 端口绑定失败异常 */ - public static boolean bindPort(final Session session, final String remoteHost, final int remotePort, final int localPort) throws JschRuntimeException { + public static boolean bindPort(final Session session, final String remoteHost, final int remotePort, final int localPort) throws SshException { return bindPort(session, remoteHost, remotePort, "127.0.0.1", localPort); } @@ -263,15 +265,15 @@ public class JschUtil { * @param localHost 本地主机 * @param localPort 本地端口 * @return 成功与否 - * @throws JschRuntimeException 端口绑定失败异常 + * @throws SshException 端口绑定失败异常 * @since 5.7.8 */ - public static boolean bindPort(final Session session, final String remoteHost, final int remotePort, final String localHost, final int localPort) throws JschRuntimeException { + public static boolean bindPort(final Session session, final String remoteHost, final int remotePort, final String localHost, final int localPort) throws SshException { if (session != null && session.isConnected()) { try { session.setPortForwardingL(localHost, localPort, remoteHost, remotePort); } catch (final JSchException e) { - throw new JschRuntimeException(e, "From [{}:{}] mapping to [{}:{}] error!", remoteHost, remotePort, localHost, localPort); + throw new SshException(e, "From [{}:{}] mapping to [{}:{}] error!", remoteHost, remotePort, localHost, localPort); } return true; } @@ -288,15 +290,15 @@ public class JschUtil { * @param host 转发到的host * @param port host上的端口 * @return 成功与否 - * @throws JschRuntimeException 端口绑定失败异常 + * @throws SshException 端口绑定失败异常 * @since 5.4.2 */ - public static boolean bindRemotePort(final Session session, final int bindPort, final String host, final int port) throws JschRuntimeException { + public static boolean bindRemotePort(final Session session, final int bindPort, final String host, final int port) throws SshException { if (session != null && session.isConnected()) { try { session.setPortForwardingR(bindPort, host, port); } catch (final JSchException e) { - throw new JschRuntimeException(e, "From [{}] mapping to [{}] error!", bindPort, port); + throw new SshException(e, "From [{}] mapping to [{}] error!", bindPort, port); } return true; } @@ -314,7 +316,7 @@ public class JschUtil { try { session.delPortForwardingL(localPort); } catch (final JSchException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } } @@ -325,9 +327,9 @@ public class JschUtil { * @param remoteHost 远程主机 * @param remotePort 远程端口 * @return 映射后的本地端口 - * @throws JschRuntimeException 连接异常 + * @throws SshException 连接异常 */ - public static int openAndBindPortToLocal(final Connector sshConn, final String remoteHost, final int remotePort) throws JschRuntimeException { + public static int openAndBindPortToLocal(final Connector sshConn, final String remoteHost, final int remotePort) throws SshException { final Session session = openSession(sshConn.getHost(), sshConn.getPort(), sshConn.getUser(), sshConn.getPassword()); final int localPort = generateLocalPort(); bindPort(session, remoteHost, remotePort, localPort); @@ -419,7 +421,7 @@ public class JschUtil { try { channel.connect(Math.max(timeout, 0)); } catch (final JSchException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } return channel; } @@ -440,7 +442,7 @@ public class JschUtil { } channel = session.openChannel(channelType.getValue()); } catch (final JSchException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } return channel; } @@ -487,7 +489,7 @@ public class JschUtil { } catch (final IOException e) { throw new IORuntimeException(e); } catch (final JSchException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } finally { IoUtil.closeQuietly(in); close(channel); diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/Sftp.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/Sftp.java similarity index 97% rename from hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/Sftp.java rename to hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/Sftp.java index ca01a0983..e2f543809 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/Sftp.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/Sftp.java @@ -10,7 +10,7 @@ * See the Mulan PSL v2 for more details. */ -package org.dromara.hutool.extra.ssh; +package org.dromara.hutool.extra.ssh.engine.jsch; import org.dromara.hutool.core.collection.CollUtil; import org.dromara.hutool.core.collection.ListUtil; @@ -26,6 +26,7 @@ import com.jcraft.jsch.Session; import com.jcraft.jsch.SftpATTRS; import com.jcraft.jsch.SftpException; import com.jcraft.jsch.SftpProgressMonitor; +import org.dromara.hutool.extra.ssh.SshException; import java.io.File; import java.io.InputStream; @@ -219,7 +220,7 @@ public class Sftp extends AbstractFtp { try { channel.setFilenameEncoding(charset.toString()); } catch (final SftpException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } this.channel = channel; } @@ -261,7 +262,7 @@ public class Sftp extends AbstractFtp { try { return getClient().pwd(); } catch (final SftpException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } } @@ -275,7 +276,7 @@ public class Sftp extends AbstractFtp { try { return getClient().getHome(); } catch (final SftpException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } } @@ -365,7 +366,7 @@ public class Sftp extends AbstractFtp { }); } catch (final SftpException e) { if (!StrUtil.startWithIgnoreCase(e.getMessage(), "No such file")) { - throw new JschRuntimeException(e); + throw new SshException(e); } // 文件不存在忽略 } @@ -382,7 +383,7 @@ public class Sftp extends AbstractFtp { getClient().mkdir(dir); return true; } catch (final SftpException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } } @@ -435,7 +436,7 @@ public class Sftp extends AbstractFtp { try { getClient().rm(filePath); } catch (final SftpException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } return true; } @@ -459,7 +460,7 @@ public class Sftp extends AbstractFtp { try { list = channel.ls(channel.pwd()); } catch (final SftpException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } String fileName; @@ -483,7 +484,7 @@ public class Sftp extends AbstractFtp { channel.rmdir(dirPath); return true; } catch (final SftpException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } } @@ -583,7 +584,7 @@ public class Sftp extends AbstractFtp { try { getClient().put(srcFilePath, destPath, monitor, mode.ordinal()); } catch (final SftpException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } return this; } @@ -602,7 +603,7 @@ public class Sftp extends AbstractFtp { try { getClient().put(srcStream, destPath, monitor, mode.ordinal()); } catch (final SftpException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } return this; } @@ -630,7 +631,7 @@ public class Sftp extends AbstractFtp { * @param destDir 本地目录 */ @Override - public void recursiveDownloadFolder(final String sourcePath, final File destDir) throws JschRuntimeException { + public void recursiveDownloadFolder(final String sourcePath, final File destDir) throws SshException { String fileName; String srcFile; File destFile; @@ -665,7 +666,7 @@ public class Sftp extends AbstractFtp { try { getClient().get(src, dest); } catch (final SftpException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } return this; } @@ -682,7 +683,7 @@ public class Sftp extends AbstractFtp { try { getClient().get(src, out); } catch (final SftpException e) { - throw new JschRuntimeException(e); + throw new SshException(e); } return this; } diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/package-info.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/package-info.java index 05812837a..c795f94d4 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/package-info.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/jsch/package-info.java @@ -11,7 +11,7 @@ */ /** - * SSH 引擎封装 + * Jsch(http://www.jcraft.com/jsch/) 引擎封装 * * @author looly */ diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/SshjSftp.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/sshj/SshjSftp.java similarity index 99% rename from hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/SshjSftp.java rename to hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/sshj/SshjSftp.java index 259b1aefa..80e80baef 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/SshjSftp.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/ssh/engine/sshj/SshjSftp.java @@ -10,7 +10,7 @@ * See the Mulan PSL v2 for more details. */ -package org.dromara.hutool.extra.ssh; +package org.dromara.hutool.extra.ssh.engine.sshj; import org.dromara.hutool.core.collection.CollUtil; import org.dromara.hutool.core.io.IoUtil; diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/tokenizer/TokenizerUtil.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/tokenizer/TokenizerUtil.java index 6c9b3bb04..bed29c21d 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/tokenizer/TokenizerUtil.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/tokenizer/TokenizerUtil.java @@ -41,4 +41,14 @@ public class TokenizerUtil { public static TokenizerEngine getEngine() { return TokenizerEngineFactory.getEngine(); } + + /** + * 创建对应名称的分词引擎对象 + * + * @param engineName 引擎名称 + * @return {@link TokenizerEngine} + */ + public static TokenizerEngine createEngine(final String engineName) { + return TokenizerEngineFactory.createEngine(engineName); + } } diff --git a/hutool-extra/src/main/java/org/dromara/hutool/extra/tokenizer/engine/TokenizerEngineFactory.java b/hutool-extra/src/main/java/org/dromara/hutool/extra/tokenizer/engine/TokenizerEngineFactory.java index df9d966d6..afee973b7 100644 --- a/hutool-extra/src/main/java/org/dromara/hutool/extra/tokenizer/engine/TokenizerEngineFactory.java +++ b/hutool-extra/src/main/java/org/dromara/hutool/extra/tokenizer/engine/TokenizerEngineFactory.java @@ -13,6 +13,7 @@ package org.dromara.hutool.extra.tokenizer.engine; import org.dromara.hutool.core.lang.Singleton; +import org.dromara.hutool.core.spi.ServiceLoader; import org.dromara.hutool.core.spi.SpiUtil; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.extra.tokenizer.TokenizerException; @@ -31,7 +32,7 @@ public class TokenizerEngineFactory { * * @return 单例的TokenizerEngine */ - public static TokenizerEngine getEngine(){ + public static TokenizerEngine getEngine() { final TokenizerEngine engine = Singleton.get(TokenizerEngine.class.getName(), TokenizerEngineFactory::createEngine); LogUtil.debug("Use [{}] Tokenizer Engine As Default.", StrUtil.removeSuffix(engine.getClass().getSimpleName(), "Engine")); return engine; @@ -46,6 +47,26 @@ public class TokenizerEngineFactory { return doCreateEngine(); } + /** + * 创建自定义引擎 + * + * @param engineName 引擎名称,忽略大小写,如`Analysis`、`Ansj`、`HanLP`、`IKAnalyzer`、`Jcseg`、`Jieba`、`Mmseg`、`Mynlp`、`Word` + * @return 引擎 + * @throws TokenizerException 无对应名称的引擎 + */ + public static TokenizerEngine createEngine(String engineName) throws TokenizerException { + if (!StrUtil.endWithIgnoreCase(engineName, "Engine")) { + engineName = engineName + "Engine"; + } + final ServiceLoader list = SpiUtil.loadList(TokenizerEngine.class); + for (final String serviceName : list.getServiceNames()) { + if (StrUtil.endWithIgnoreCase(serviceName, engineName)) { + return list.getService(serviceName); + } + } + throw new TokenizerException("No such engine named: " + engineName); + } + /** * 根据用户引入的分词引擎jar,自动创建对应的分词引擎对象 * @@ -53,7 +74,7 @@ public class TokenizerEngineFactory { */ private static TokenizerEngine doCreateEngine() { final TokenizerEngine engine = SpiUtil.loadFirstAvailable(TokenizerEngine.class); - if(null != engine){ + if (null != engine) { return engine; } diff --git a/hutool-extra/src/main/resources/META-INF/services/org.dromara.hutool.extra.pinyin.engine.PinyinEngine b/hutool-extra/src/main/resources/META-INF/services/org.dromara.hutool.extra.pinyin.engine.PinyinEngine index b6764a734..07e159b18 100644 --- a/hutool-extra/src/main/resources/META-INF/services/org.dromara.hutool.extra.pinyin.engine.PinyinEngine +++ b/hutool-extra/src/main/resources/META-INF/services/org.dromara.hutool.extra.pinyin.engine.PinyinEngine @@ -14,4 +14,4 @@ org.dromara.hutool.extra.pinyin.engine.tinypinyin.TinyPinyinEngine org.dromara.hutool.extra.pinyin.engine.jpinyin.JPinyinEngine org.dromara.hutool.extra.pinyin.engine.pinyin4j.Pinyin4jEngine org.dromara.hutool.extra.pinyin.engine.bopomofo4j.Bopomofo4jEngine -org.dromara.hutool.extra.pinyin.engine.houbbpinyin.HoubbPinyinEngine +org.dromara.hutool.extra.pinyin.engine.houbb.HoubbEngine diff --git a/hutool-extra/src/test/java/org/dromara/hutool/extra/ftp/FtpTest.java b/hutool-extra/src/test/java/org/dromara/hutool/extra/ftp/FtpTest.java index 50b8882c8..10d95be66 100644 --- a/hutool-extra/src/test/java/org/dromara/hutool/extra/ftp/FtpTest.java +++ b/hutool-extra/src/test/java/org/dromara/hutool/extra/ftp/FtpTest.java @@ -16,7 +16,7 @@ import org.apache.commons.net.ftp.FTPSClient; import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.io.file.FileUtil; import org.dromara.hutool.core.lang.Console; -import org.dromara.hutool.extra.ssh.Sftp; +import org.dromara.hutool.extra.ssh.engine.jsch.Sftp; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/Bopomofo4jTest.java b/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/Bopomofo4jTest.java index 45e014e91..1b0f0da53 100644 --- a/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/Bopomofo4jTest.java +++ b/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/Bopomofo4jTest.java @@ -12,13 +12,13 @@ package org.dromara.hutool.extra.pinyin; -import org.dromara.hutool.extra.pinyin.engine.bopomofo4j.Bopomofo4jEngine; +import org.dromara.hutool.extra.pinyin.engine.PinyinEngine; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class Bopomofo4jTest { - final Bopomofo4jEngine engine = new Bopomofo4jEngine(); + final PinyinEngine engine = PinyinUtil.createEngine("bopomofo4j"); @Test public void getFirstLetterByBopomofo4jTest(){ diff --git a/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/HoubbPinyinTest.java b/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/HoubbPinyinTest.java index 8a0baee76..948f0504a 100644 --- a/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/HoubbPinyinTest.java +++ b/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/HoubbPinyinTest.java @@ -12,13 +12,13 @@ package org.dromara.hutool.extra.pinyin; -import org.dromara.hutool.extra.pinyin.engine.houbbpinyin.HoubbPinyinEngine; +import org.dromara.hutool.extra.pinyin.engine.PinyinEngine; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class HoubbPinyinTest { - final HoubbPinyinEngine engine = new HoubbPinyinEngine(); + final PinyinEngine engine = PinyinUtil.createEngine("houbb"); @Test public void getFirstLetterTest(){ diff --git a/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/JpinyinTest.java b/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/JpinyinTest.java index e498acf24..adda45c43 100644 --- a/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/JpinyinTest.java +++ b/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/JpinyinTest.java @@ -12,13 +12,13 @@ package org.dromara.hutool.extra.pinyin; -import org.dromara.hutool.extra.pinyin.engine.jpinyin.JPinyinEngine; +import org.dromara.hutool.extra.pinyin.engine.PinyinEngine; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class JpinyinTest { - final JPinyinEngine engine = new JPinyinEngine(); + final PinyinEngine engine = PinyinUtil.createEngine("jpinyin"); @Test public void getFirstLetterByPinyin4jTest(){ diff --git a/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/Pinyin4jTest.java b/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/Pinyin4jTest.java index 37acb1d38..ae1ec5a4a 100644 --- a/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/Pinyin4jTest.java +++ b/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/Pinyin4jTest.java @@ -12,13 +12,13 @@ package org.dromara.hutool.extra.pinyin; -import org.dromara.hutool.extra.pinyin.engine.pinyin4j.Pinyin4jEngine; +import org.dromara.hutool.extra.pinyin.engine.PinyinEngine; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class Pinyin4jTest { - final Pinyin4jEngine engine = new Pinyin4jEngine(); + final PinyinEngine engine = PinyinUtil.createEngine("pinyin4j"); @Test public void getFirstLetterByPinyin4jTest(){ diff --git a/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/TinyPinyinTest.java b/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/TinyPinyinTest.java index 7aae54849..320eca738 100644 --- a/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/TinyPinyinTest.java +++ b/hutool-extra/src/test/java/org/dromara/hutool/extra/pinyin/TinyPinyinTest.java @@ -12,13 +12,13 @@ package org.dromara.hutool.extra.pinyin; -import org.dromara.hutool.extra.pinyin.engine.tinypinyin.TinyPinyinEngine; +import org.dromara.hutool.extra.pinyin.engine.PinyinEngine; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; public class TinyPinyinTest { - final TinyPinyinEngine engine = new TinyPinyinEngine(); + final PinyinEngine engine = PinyinUtil.createEngine("tinypinyin"); @Test public void getFirstLetterByPinyin4jTest(){ diff --git a/hutool-extra/src/test/java/org/dromara/hutool/extra/ssh/JschUtilTest.java b/hutool-extra/src/test/java/org/dromara/hutool/extra/ssh/JschUtilTest.java index 7c83af49a..76556ba22 100644 --- a/hutool-extra/src/test/java/org/dromara/hutool/extra/ssh/JschUtilTest.java +++ b/hutool-extra/src/test/java/org/dromara/hutool/extra/ssh/JschUtilTest.java @@ -15,6 +15,8 @@ package org.dromara.hutool.extra.ssh; import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.lang.Console; import com.jcraft.jsch.Session; +import org.dromara.hutool.extra.ssh.engine.jsch.JschUtil; +import org.dromara.hutool.extra.ssh.engine.jsch.Sftp; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -74,7 +76,7 @@ public class JschUtilTest { Console.log("isConnected " + sftp.getClient().isConnected()); Console.log("打印pwd: " + sftp.pwd()); Console.log("cd / : " + sftp.cd("/")); - }catch (final JschRuntimeException e) { + }catch (final SshException e) { e.printStackTrace(); } diff --git a/hutool-extra/src/test/java/org/dromara/hutool/extra/ssh/SftpTest.java b/hutool-extra/src/test/java/org/dromara/hutool/extra/ssh/SftpTest.java index 2a361822c..7e887c1d1 100644 --- a/hutool-extra/src/test/java/org/dromara/hutool/extra/ssh/SftpTest.java +++ b/hutool-extra/src/test/java/org/dromara/hutool/extra/ssh/SftpTest.java @@ -13,6 +13,7 @@ package org.dromara.hutool.extra.ssh; import org.dromara.hutool.core.util.CharsetUtil; +import org.dromara.hutool.extra.ssh.engine.sshj.SshjSftp; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/hutool-extra/src/test/java/org/dromara/hutool/extra/tokenizer/TokenizerUtilTest.java b/hutool-extra/src/test/java/org/dromara/hutool/extra/tokenizer/TokenizerUtilTest.java index 1bc82bf94..c2df63bc9 100644 --- a/hutool-extra/src/test/java/org/dromara/hutool/extra/tokenizer/TokenizerUtilTest.java +++ b/hutool-extra/src/test/java/org/dromara/hutool/extra/tokenizer/TokenizerUtilTest.java @@ -14,14 +14,6 @@ package org.dromara.hutool.extra.tokenizer; import org.dromara.hutool.core.collection.iter.IterUtil; import org.dromara.hutool.extra.tokenizer.engine.TokenizerEngine; -import org.dromara.hutool.extra.tokenizer.engine.analysis.SmartcnEngine; -import org.dromara.hutool.extra.tokenizer.engine.hanlp.HanLPEngine; -import org.dromara.hutool.extra.tokenizer.engine.ikanalyzer.IKAnalyzerEngine; -import org.dromara.hutool.extra.tokenizer.engine.jcseg.JcsegEngine; -import org.dromara.hutool.extra.tokenizer.engine.jieba.JiebaEngine; -import org.dromara.hutool.extra.tokenizer.engine.mmseg.MmsegEngine; -import org.dromara.hutool.extra.tokenizer.engine.mynlp.MynlpEngine; -import org.dromara.hutool.extra.tokenizer.engine.word.WordEngine; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -51,7 +43,7 @@ public class TokenizerUtilTest { @Test public void hanlpTest() { - final TokenizerEngine engine = new HanLPEngine(); + final TokenizerEngine engine = TokenizerUtil.createEngine("hanlp"); final Result result = engine.parse(text); final String resultStr = IterUtil.join(result, " "); Assertions.assertEquals("这 两 个 方法 的 区别 在于 返回 值", resultStr); @@ -59,7 +51,7 @@ public class TokenizerUtilTest { @Test public void ikAnalyzerTest() { - final TokenizerEngine engine = new IKAnalyzerEngine(); + final TokenizerEngine engine = TokenizerUtil.createEngine("IKAnalyzer"); final Result result = engine.parse(text); final String resultStr = IterUtil.join(result, " "); Assertions.assertEquals("这两个 方法 的 区别 在于 返回值", resultStr); @@ -67,14 +59,14 @@ public class TokenizerUtilTest { @Test public void jcsegTest() { - final TokenizerEngine engine = new JcsegEngine(); + final TokenizerEngine engine = TokenizerUtil.createEngine("Jcseg"); final Result result = engine.parse(text); checkResult(result); } @Test public void jiebaTest() { - final TokenizerEngine engine = new JiebaEngine(); + final TokenizerEngine engine = TokenizerUtil.createEngine("Jieba"); final Result result = engine.parse(text); final String resultStr = IterUtil.join(result, " "); Assertions.assertEquals("这 两个 方法 的 区别 在于 返回值", resultStr); @@ -82,14 +74,14 @@ public class TokenizerUtilTest { @Test public void mmsegTest() { - final TokenizerEngine engine = new MmsegEngine(); + final TokenizerEngine engine = TokenizerUtil.createEngine("Mmseg"); final Result result = engine.parse(text); checkResult(result); } @Test public void smartcnTest() { - final TokenizerEngine engine = new SmartcnEngine(); + final TokenizerEngine engine = TokenizerUtil.createEngine("Smartcn"); final Result result = engine.parse(text); final String resultStr = IterUtil.join(result, " "); Assertions.assertEquals("这 两 个 方法 的 区别 在于 返回 值", resultStr); @@ -97,7 +89,7 @@ public class TokenizerUtilTest { @Test public void wordTest() { - final TokenizerEngine engine = new WordEngine(); + final TokenizerEngine engine = TokenizerUtil.createEngine("Word"); final Result result = engine.parse(text); final String resultStr = IterUtil.join(result, " "); Assertions.assertEquals("这两个 方法 的 区别 在于 返回值", resultStr); @@ -105,7 +97,7 @@ public class TokenizerUtilTest { @Test public void mynlpTest() { - final TokenizerEngine engine = new MynlpEngine(); + final TokenizerEngine engine = TokenizerUtil.createEngine("Mynlp"); final Result result = engine.parse(text); final String resultStr = IterUtil.join(result, " "); Assertions.assertEquals("这 两个 方法 的 区别 在于 返回 值", resultStr); diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/HttpUtil.java b/hutool-http/src/main/java/org/dromara/hutool/http/HttpUtil.java index 6fbf5eb85..781f2d8d6 100644 --- a/hutool-http/src/main/java/org/dromara/hutool/http/HttpUtil.java +++ b/hutool-http/src/main/java/org/dromara/hutool/http/HttpUtil.java @@ -17,6 +17,7 @@ import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.http.client.ClientConfig; import org.dromara.hutool.http.client.Request; import org.dromara.hutool.http.client.Response; +import org.dromara.hutool.http.client.engine.ClientEngine; import org.dromara.hutool.http.client.engine.ClientEngineFactory; import org.dromara.hutool.http.meta.Method; import org.dromara.hutool.http.server.SimpleServer; @@ -85,8 +86,8 @@ public class HttpUtil { @SuppressWarnings("resource") public static String get(final String urlString, final int timeout) { return ClientEngineFactory.getEngine() - .init(ClientConfig.of().setConnectionTimeout(timeout).setReadTimeout(timeout)) - .send(Request.of(urlString)).bodyStr(); + .init(ClientConfig.of().setConnectionTimeout(timeout).setReadTimeout(timeout)) + .send(Request.of(urlString)).bodyStr(); } /** @@ -99,7 +100,7 @@ public class HttpUtil { @SuppressWarnings("resource") public static String get(final String urlString, final Map paramMap) { return send(Request.of(urlString).form(paramMap)) - .bodyStr(); + .bodyStr(); } /** @@ -112,7 +113,7 @@ public class HttpUtil { @SuppressWarnings("resource") public static String post(final String urlString, final Map paramMap) { return send(Request.of(urlString).method(Method.POST).form(paramMap)) - .bodyStr(); + .bodyStr(); } /** @@ -131,7 +132,7 @@ public class HttpUtil { @SuppressWarnings("resource") public static String post(final String urlString, final String body) { return send(Request.of(urlString).method(Method.POST).body(body)) - .bodyStr(); + .bodyStr(); } /** @@ -140,18 +141,18 @@ public class HttpUtil { * @param request HTTP请求 * @return HTTP响应 */ - public static Response send(final Request request){ + public static Response send(final Request request) { return ClientEngineFactory.getEngine().send(request); } /** * 将表单数据加到URL中(用于GET表单提交) * 表单的键值对会被url编码,但是url中原参数不会被编码 - * 且对form参数进行 FormUrlEncoded ,x-www-form-urlencoded模式,此模式下空格会编码为'+' + * 且对form参数进行 FormUrlEncoded ,x-www-form-urlencoded模式,此模式下空格会编码为'+' * - * @param url URL - * @param form 表单数据 - * @param charset 编码 null表示不encode键值对 + * @param url URL + * @param form 表单数据 + * @param charset 编码 null表示不encode键值对 * @return 合成后的URL */ public static String urlWithFormUrlEncoded(final String url, final Map form, final Charset charset) { @@ -213,6 +214,16 @@ public class HttpUtil { return urlBuilder.toString(); } + /** + * 创建客户端引擎 + * + * @param engineName 引擎名称 + * @return {@link ClientEngine} + */ + public static ClientEngine createClient(final String engineName) { + return ClientEngineFactory.createEngine(engineName); + } + /** * 创建简易的Http服务器 * diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/ClientEngineFactory.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/ClientEngineFactory.java index 75937181d..a39ae0564 100644 --- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/ClientEngineFactory.java +++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/ClientEngineFactory.java @@ -13,6 +13,7 @@ package org.dromara.hutool.http.client.engine; import org.dromara.hutool.core.lang.Singleton; +import org.dromara.hutool.core.spi.ServiceLoader; import org.dromara.hutool.core.spi.SpiUtil; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.http.HttpException; @@ -48,6 +49,26 @@ public class ClientEngineFactory { return createEngine().init(config); } + /** + * 创建自定义引擎 + * + * @param engineName 引擎名称,忽略大小写,如`HttpClient4`、`HttpClient5`、`OkHttp`、`JdkClient` + * @return 引擎 + * @throws HttpException 无对应名称的引擎 + */ + public static ClientEngine createEngine(String engineName) throws HttpException { + if (!StrUtil.endWithIgnoreCase(engineName, "Engine")) { + engineName = engineName + "Engine"; + } + final ServiceLoader list = SpiUtil.loadList(ClientEngine.class); + for (final String serviceName : list.getServiceNames()) { + if (StrUtil.endWithIgnoreCase(serviceName, engineName)) { + return list.getService(serviceName); + } + } + throw new HttpException("No such engine named: " + engineName); + } + /** * 根据用户引入的HTTP客户端引擎jar,自动创建对应的拼音引擎对象
* 推荐创建的引擎单例使用,此方法每次调用会返回新的引擎 diff --git a/hutool-http/src/test/java/org/dromara/hutool/http/client/HttpClient4EngineTest.java b/hutool-http/src/test/java/org/dromara/hutool/http/client/HttpClient4EngineTest.java index dce09cec8..9851f87f3 100644 --- a/hutool-http/src/test/java/org/dromara/hutool/http/client/HttpClient4EngineTest.java +++ b/hutool-http/src/test/java/org/dromara/hutool/http/client/HttpClient4EngineTest.java @@ -13,8 +13,8 @@ package org.dromara.hutool.http.client; import org.dromara.hutool.core.lang.Console; +import org.dromara.hutool.http.HttpUtil; import org.dromara.hutool.http.client.engine.ClientEngine; -import org.dromara.hutool.http.client.engine.httpclient4.HttpClient4Engine; import org.dromara.hutool.http.meta.Method; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -25,7 +25,7 @@ public class HttpClient4EngineTest { @Test @Disabled public void getTest() { - final ClientEngine engine = new HttpClient4Engine(); + final ClientEngine engine = HttpUtil.createClient("httpclient4"); final Request req = Request.of("https://www.hutool.cn/").method(Method.GET); final Response res = engine.send(req); diff --git a/hutool-http/src/test/java/org/dromara/hutool/http/client/HttpClient5EngineTest.java b/hutool-http/src/test/java/org/dromara/hutool/http/client/HttpClient5EngineTest.java index dd92ccb45..4d3126a7b 100644 --- a/hutool-http/src/test/java/org/dromara/hutool/http/client/HttpClient5EngineTest.java +++ b/hutool-http/src/test/java/org/dromara/hutool/http/client/HttpClient5EngineTest.java @@ -13,8 +13,8 @@ package org.dromara.hutool.http.client; import org.dromara.hutool.core.lang.Console; +import org.dromara.hutool.http.HttpUtil; import org.dromara.hutool.http.client.engine.ClientEngine; -import org.dromara.hutool.http.client.engine.httpclient5.HttpClient5Engine; import org.dromara.hutool.http.meta.Method; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -25,7 +25,7 @@ public class HttpClient5EngineTest { @Test @Disabled public void getTest() { - final ClientEngine engine = new HttpClient5Engine(); + final ClientEngine engine = HttpUtil.createClient("httpclient5"); final Request req = Request.of("https://www.hutool.cn/").method(Method.GET); final Response res = engine.send(req); diff --git a/hutool-http/src/test/java/org/dromara/hutool/http/client/Issue3240Test.java b/hutool-http/src/test/java/org/dromara/hutool/http/client/Issue3240Test.java index 64712da70..d758e3181 100755 --- a/hutool-http/src/test/java/org/dromara/hutool/http/client/Issue3240Test.java +++ b/hutool-http/src/test/java/org/dromara/hutool/http/client/Issue3240Test.java @@ -13,9 +13,8 @@ package org.dromara.hutool.http.client; import org.dromara.hutool.core.lang.Console; +import org.dromara.hutool.http.HttpUtil; import org.dromara.hutool.http.client.engine.ClientEngine; -import org.dromara.hutool.http.client.engine.httpclient4.HttpClient4Engine; -import org.dromara.hutool.http.client.engine.okhttp.OkHttpEngine; import org.dromara.hutool.http.meta.Method; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -25,7 +24,7 @@ public class Issue3240Test { @Disabled void okHttpTest() { String url = "https://gh.yubue.cn/https://github.com/espressif/arduino-esp32/releases/download/2.0.11/package_esp32_dev_index.json"; - final ClientEngine engine = new OkHttpEngine(); + final ClientEngine engine = HttpUtil.createClient("okhttp"); final Response send = engine.send(Request.of(url).method(Method.GET)); Console.log(send.body().getString()); } @@ -34,7 +33,7 @@ public class Issue3240Test { @Disabled void httpClient4Test() { String url = "https://gh.yubue.cn/https://github.com/espressif/arduino-esp32/releases/download/2.0.11/package_esp32_dev_index.json"; - final ClientEngine engine = new HttpClient4Engine(); + final ClientEngine engine = HttpUtil.createClient("okhttp"); final Response send = engine.send(Request.of(url).method(Method.GET)); Console.log(send.body().getString()); } diff --git a/hutool-http/src/test/java/org/dromara/hutool/http/client/IssueI7ZRJUTest.java b/hutool-http/src/test/java/org/dromara/hutool/http/client/IssueI7ZRJUTest.java index 0afd06968..ce7da22df 100755 --- a/hutool-http/src/test/java/org/dromara/hutool/http/client/IssueI7ZRJUTest.java +++ b/hutool-http/src/test/java/org/dromara/hutool/http/client/IssueI7ZRJUTest.java @@ -20,7 +20,7 @@ import org.junit.jupiter.api.Test; public class IssueI7ZRJUTest { - @SuppressWarnings({"resource", "TestFailedLine"}) + @SuppressWarnings({"resource"}) @Test @Disabled void getBadSSlTest() { diff --git a/hutool-http/src/test/java/org/dromara/hutool/http/client/JdkEngineTest.java b/hutool-http/src/test/java/org/dromara/hutool/http/client/JdkEngineTest.java index de5e93ca1..b4d77aa9c 100644 --- a/hutool-http/src/test/java/org/dromara/hutool/http/client/JdkEngineTest.java +++ b/hutool-http/src/test/java/org/dromara/hutool/http/client/JdkEngineTest.java @@ -13,8 +13,8 @@ package org.dromara.hutool.http.client; import org.dromara.hutool.core.lang.Console; +import org.dromara.hutool.http.HttpUtil; import org.dromara.hutool.http.client.engine.ClientEngine; -import org.dromara.hutool.http.client.engine.jdk.JdkClientEngine; import org.dromara.hutool.http.meta.Method; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -24,7 +24,7 @@ public class JdkEngineTest { @Test @Disabled public void getTest(){ - final ClientEngine engine = new JdkClientEngine(); + final ClientEngine engine = HttpUtil.createClient("jdkClient"); final Request req = Request.of("https://www.hutool.cn/").method(Method.GET); final Response res = engine.send(req);