Browse Source

Pull request #4427: REPORT-53016 插件缺失提醒-设计改进-白名单和远程提醒

Merge in DESIGN/design from ~VITO/c-design:feature/10.0 to feature/10.0

* commit '85426ed7bfbddd52e702b12c0bebd5646a1c9ebc':
  REPORT-53016 插件缺失提醒-设计改进-白名单和远程提醒
feature/10.0
ju|剧浩宇 4 years ago
parent
commit
4f8aa5a21c
  1. 132
      designer-realize/src/main/java/com/fr/design/mainframe/app/DesignerAppUtils.java
  2. 19
      designer-realize/src/main/java/com/fr/design/mainframe/app/FormApp.java
  3. 106
      designer-realize/src/test/java/com/fr/design/mainframe/app/DesignerAppUtilsTest.java

132
designer-realize/src/main/java/com/fr/design/mainframe/app/DesignerAppUtils.java

@ -7,16 +7,24 @@ import com.fr.design.extra.exe.callback.ModifyStatusCallback;
import com.fr.design.i18n.Toolkit;
import com.fr.design.mainframe.DesignerContext;
import com.fr.design.ui.util.UIUtil;
import com.fr.io.TemplateIOErrorUtils;
import com.fr.locale.InterProviderFactory;
import com.fr.plugin.context.PluginMarker;
import com.fr.plugin.context.PluginMarkerAdapter;
import com.fr.plugin.engine.remote.PluginRemoteSync;
import com.fr.plugin.manage.PluginManager;
import com.fr.plugin.manage.control.PluginControllerHelper;
import com.fr.plugin.manage.control.PluginTask;
import com.fr.stable.StringUtils;
import com.fr.stable.TemplateIOErrorContextHolder;
import com.fr.third.guava.cache.Cache;
import com.fr.third.guava.cache.CacheBuilder;
import com.fr.third.guava.collect.Multimap;
import com.fr.workspace.WorkContext;
import java.time.Duration;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 设计器app共用工具类
@ -26,6 +34,118 @@ import java.util.Collection;
* Created by vito on 2021/5/27
*/
public class DesignerAppUtils {
private static final int DEFAULT_MAX_CACHE_SIZE = 50;
private static final int DEFAULT_CONCURRENCY_LEVEL = 8;
private static final long DEFAULT_EXPIRE_HOURS = 1;
private static final Cache<String, Multimap<String, PluginMarkerAdapter>> ERROR_CACHE = CacheBuilder.newBuilder()
.maximumSize(DEFAULT_MAX_CACHE_SIZE)
.expireAfterAccess(Duration.ofHours(DEFAULT_EXPIRE_HOURS))
.concurrencyLevel(DEFAULT_CONCURRENCY_LEVEL)
.build();
/**
* 弹出指定的插件信息
* 并失效缓存
*
* @param key 指定key
* @return 插件安装信息
*/
public static Multimap<String, PluginMarkerAdapter> popPluginInfoMap(String key) {
Multimap<String, PluginMarkerAdapter> ifPresent = ERROR_CACHE.getIfPresent(key);
ERROR_CACHE.invalidate(key);
return ifPresent;
}
/**
* 失效指定的插件信息缓存
*
* @param key 指定key
*/
public static void invalidatePlugins(String key) {
ERROR_CACHE.invalidate(key);
}
/**
* 格式化多行插件错误信息详情并缓存
* 用于界面展示
*
* @param key 缓存key
* @return 格式化后的多行插件错误信息详情
*/
public static String dealWithErrorDetailMultiLineAndCache(String key) {
Multimap<String, PluginMarkerAdapter> pendingPlugins = TemplateIOErrorContextHolder.getPendingPlugin();
if (pendingPlugins.isEmpty()) {
return StringUtils.EMPTY;
}
dealWithRemote(pendingPlugins);
StringBuilder sb = new StringBuilder();
if (WorkContext.getCurrent().isLocal()) {
// 缓存等待后续处理
ERROR_CACHE.put(key, pendingPlugins);
}
Collection<PluginMarkerAdapter> unknownPlugins = pendingPlugins.get(TemplateIOErrorContextHolder.UNKNOWN_PLUGIN);
if (!unknownPlugins.isEmpty()) {
sb.append(InterProviderFactory.getProvider().getLocText("Fine-Core_Plugin_Error_UnknownPlugin")).append(":\n");
for (PluginMarkerAdapter pluginMarker : unknownPlugins) {
sb.append("\"").append(pluginMarker.getPluginID()).append("\"")
.append(InterProviderFactory.getProvider().getLocText("Fine-Dec_Platform_Plugin")).append('\n');
}
}
Collection<PluginMarkerAdapter> notInstalledPlugins = pendingPlugins.get(TemplateIOErrorContextHolder.NOT_INSTALLED_PLUGIN);
if (!notInstalledPlugins.isEmpty()) {
sb.append(InterProviderFactory.getProvider().getLocText("Fine-Core_Plugin_Error_UninstalledPlugins")).append(":\n");
for (PluginMarkerAdapter pluginMarker : notInstalledPlugins) {
sb.append("\"").append(pluginMarker.getPluginName()).append("\"")
.append(InterProviderFactory.getProvider().getLocText("Fine-Dec_Platform_Plugin")).append('\n');
}
}
Collection<PluginMarkerAdapter> disablePlugins = pendingPlugins.get(TemplateIOErrorContextHolder.DISABLE_PLUGIN);
if (!disablePlugins.isEmpty()) {
sb.append(InterProviderFactory.getProvider().getLocText("Fine-Core_Plugin_Error_InactivePlugins")).append(":\n");
for (PluginMarkerAdapter pluginMarker : disablePlugins) {
sb.append("\"").append(pluginMarker.getPluginName()).append("\"")
.append(InterProviderFactory.getProvider().getLocText("Fine-Dec_Platform_Plugin")).append('\n');
}
}
return sb.toString();
}
/**
* 远程环境下需要特殊处理远程服务器尚未安装的插件
*
* @param pendingPlugins 待处理插件
*/
private static void dealWithRemote(Multimap<String, PluginMarkerAdapter> pendingPlugins) {
if (!WorkContext.getCurrent().isLocal()) {
rearrange(pendingPlugins);
}
}
/**
* 远程设计重新整理下列表
*
* @param pendingPlugins 待处理列表
*/
public static void rearrange(Multimap<String, PluginMarkerAdapter> pendingPlugins) {
Map<String, PluginRemoteSync.PluginStatus> pluginRemoteStatusByIdIndex = PluginRemoteSync.getInstance().getPluginRemoteStatusByIdIndex();
Collection<PluginMarkerAdapter> unknownPlugins = pendingPlugins.get(TemplateIOErrorContextHolder.UNKNOWN_PLUGIN);
Collection<PluginMarkerAdapter> notInstall = pendingPlugins.get(TemplateIOErrorContextHolder.NOT_INSTALLED_PLUGIN);
Collection<PluginMarkerAdapter> disable = pendingPlugins.get(TemplateIOErrorContextHolder.DISABLE_PLUGIN);
unknownPlugins.removeIf(adapter -> pluginRemoteStatusByIdIndex.containsKey(adapter.getPluginID()));
// 本地未启用名单不准确添加到一起之后重新分配
notInstall.addAll(disable);
disable.clear();
// 从所有未安装中过滤远程未启用的,添加到未启用列表
disable.addAll(notInstall.stream().filter(plugin ->
pluginRemoteStatusByIdIndex.containsKey(plugin.getPluginID())
&& !pluginRemoteStatusByIdIndex.get(plugin.getPluginID()).isRunning())
.collect(Collectors.toList()));
// 清理未安装中所有远程安装过的插件(包含启用和未启用)
notInstall.removeIf(adapter -> pluginRemoteStatusByIdIndex.containsKey(adapter.getPluginID()));
}
/**
* 处理模板读取时的异常
@ -34,7 +154,7 @@ public class DesignerAppUtils {
*/
public static void dealWithTemplateIOError(String path) {
// 试图获取多行读取错误提示并缓存待处理列表
String detail = TemplateIOErrorUtils.dealWithErrorDetailMultiLineAndCache(path);
String detail = dealWithErrorDetailMultiLineAndCache(path);
if (detail.length() > 0) {
UIUtil.invokeLaterIfNeeded(() -> {
if (WorkContext.getCurrent().isLocal()) {
@ -52,7 +172,7 @@ public class DesignerAppUtils {
@Override
public void doCancel() {
TemplateIOErrorUtils.invalidatePlugins(path);
invalidatePlugins(path);
}
}).build().setVisible(true);
} else {
@ -68,13 +188,13 @@ public class DesignerAppUtils {
}
private static void installAndEnablePlugin(String key) {
Multimap<String, PluginMarkerAdapter> stringPluginMarkerAdapterMultimap = TemplateIOErrorUtils.popPluginInfoMap(key);
Collection<PluginMarkerAdapter> disablePlugins = stringPluginMarkerAdapterMultimap.get(TemplateIOErrorUtils.DISABLE_PLUGIN);
Multimap<String, PluginMarkerAdapter> stringPluginMarkerAdapterMultimap = popPluginInfoMap(key);
Collection<PluginMarkerAdapter> disablePlugins = stringPluginMarkerAdapterMultimap.get(TemplateIOErrorContextHolder.DISABLE_PLUGIN);
for (PluginMarkerAdapter disablePlugin : disablePlugins) {
PluginManager.getController().enable(disablePlugin, new ModifyStatusCallback(false));
}
Collection<PluginMarkerAdapter> uninstallPlugins = stringPluginMarkerAdapterMultimap.get(TemplateIOErrorUtils.NOT_INSTALLED_PLUGIN);
Collection<PluginMarkerAdapter> uninstallPlugins = stringPluginMarkerAdapterMultimap.get(TemplateIOErrorContextHolder.NOT_INSTALLED_PLUGIN);
for (PluginMarker uninstallPlugin : uninstallPlugins) {
PluginTask pluginTask = PluginTask.installTask(uninstallPlugin);
PluginControllerHelper.installOnline(uninstallPlugin, new InstallOnlineCallback(pluginTask));

19
designer-realize/src/main/java/com/fr/design/mainframe/app/FormApp.java

@ -23,6 +23,7 @@ import com.fr.general.ComparatorUtils;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.Constants;
import com.fr.stable.bridge.StableFactory;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.concurrent.Callable;
@ -62,8 +63,7 @@ class FormApp extends AbstractAppProvider {
new Callable<OpenResult<Form, Parameter[]>>() {
@Override
public OpenResult<Form, Parameter[]> call() throws Exception {
Form form = asIOFile(tplFile);
DesignerAppUtils.dealWithTemplateIOError(tplFile.getPath());
Form form = getForm(tplFile);
return new OpenResult<>(form, form.getParameters());
}
}, emptyForm);
@ -72,22 +72,31 @@ class FormApp extends AbstractAppProvider {
public JTemplate<?, ?> call() throws Exception {
OpenResult<Form, Parameter[]> result = worker.getResult();
return (JTemplate<Form, ?>) StableFactory.getMarkedInstanceObjectFromClass(BaseJForm.XML_TAG,
new Object[]{result.getBaseBook(), tplFile, result.getRef()}, classType, BaseJForm.class);
new Object[]{result.getBaseBook(), tplFile, result.getRef()}, classType, BaseJForm.class);
}
});
worker.start(tplFile.getPath());
OpenResult<Form, Parameter[]> result = worker.getResult();
if (result != null) {
return (JTemplate<Form, ?>) StableFactory.getMarkedInstanceObjectFromClass(BaseJForm.XML_TAG,
new Object[]{result.getBaseBook(), tplFile, new Parameter[0]}, classType, BaseJForm.class);
new Object[]{result.getBaseBook(), tplFile, new Parameter[0]}, classType, BaseJForm.class);
}
return emptyForm;
} else {
return (JTemplate<Form, ?>) StableFactory.getMarkedInstanceObjectFromClass(BaseJForm.XML_TAG,
new Object[]{asIOFile(tplFile), tplFile}, classType, BaseJForm.class);
new Object[]{getForm(tplFile), tplFile}, classType, BaseJForm.class);
}
}
@Nullable
private Form getForm(FILE tplFile) {
Form form = asIOFile(tplFile);
if (form != null) {
DesignerAppUtils.dealWithTemplateIOError(tplFile.getPath());
}
return form;
}
@Override
public Form asIOFile(FILE file) {

106
designer-realize/src/test/java/com/fr/design/mainframe/app/DesignerAppUtilsTest.java

@ -0,0 +1,106 @@
package com.fr.design.mainframe.app;
import com.fr.invoke.Reflect;
import com.fr.plugin.context.PluginMarker;
import com.fr.plugin.context.PluginMarkerAdapter;
import com.fr.plugin.engine.remote.PluginRemoteSync;
import com.fr.stable.TemplateIOErrorContextHolder;
import com.fr.third.guava.collect.Multimap;
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
/**
* @author vito
* @version 10.0
* Created by vito on 2021/5/31
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({PluginRemoteSync.class})
public class DesignerAppUtilsTest {
@Test
public void testDealWithErrorDetailMultiLineAndCache() {
TemplateIOErrorContextHolder.registerPluginNameMap(new HashMap<String, String>() {{
put("2", "好用的插件");
}},new HashSet<>());
TemplateIOErrorContextHolder.addNeedEnablePlugin(PluginMarkerAdapter.create("1", "1.0", "1插件"));
TemplateIOErrorContextHolder.addNeedInstallPlugin(PluginMarker.create("2", "1.0"));
TemplateIOErrorContextHolder.addNeedInstallPlugin(PluginMarker.create("3", "1.0"));
String log = DesignerAppUtils.dealWithErrorDetailMultiLineAndCache("template1");
Assert.assertTrue(log.contains("1插件"));
Assert.assertTrue(log.contains("好用的插件"));
Assert.assertTrue(log.contains("3"));
Multimap<String, PluginMarkerAdapter> map = DesignerAppUtils.popPluginInfoMap("template1");
Assert.assertEquals(3, map.size());
Assert.assertNull(DesignerAppUtils.popPluginInfoMap("template1"));
}
@Test
public void testInvalidatePlugins() {
TemplateIOErrorContextHolder.registerPluginNameMap(new HashMap<String, String>() {{
put("2", "好用的插件");
}},new HashSet<>());
TemplateIOErrorContextHolder.addNeedEnablePlugin(PluginMarkerAdapter.create("1", "1.0", "1插件"));
TemplateIOErrorContextHolder.addNeedInstallPlugin(PluginMarker.create("2", "1.0"));
TemplateIOErrorContextHolder.addNeedInstallPlugin(PluginMarker.create("3", "1.0"));
String log = DesignerAppUtils.dealWithErrorDetailMultiLineAndCache("template1");
Assert.assertTrue(log.contains("1插件"));
Assert.assertTrue(log.contains("好用的插件"));
Assert.assertTrue(log.contains("3"));
DesignerAppUtils.invalidatePlugins("template1");
Assert.assertNull(DesignerAppUtils.popPluginInfoMap("template1"));
}
@Test
public void testRearrange(){
// 远程插件模拟注册
PluginRemoteSync pluginRemoteSync = EasyMock.createMock(PluginRemoteSync.class);
EasyMock.expect(pluginRemoteSync.getPluginRemoteStatusByIdIndex()).andReturn(new HashMap<String, PluginRemoteSync.PluginStatus>(){{
put("com.fr.plugin1", Reflect.on(PluginRemoteSync.PluginStatus.class).call("create","com.fr.plugin1","1",true).get());
put("com.fr.plugin2", Reflect.on(PluginRemoteSync.PluginStatus.class).call("create","com.fr.plugin2","1",true).get());
put("com.fr.plugin3", Reflect.on(PluginRemoteSync.PluginStatus.class).call("create","com.fr.plugin3","1",false).get());
put("com.fr.plugin4", Reflect.on(PluginRemoteSync.PluginStatus.class).call("create","com.fr.plugin4","1",false).get());
}}).anyTimes();
EasyMock.replay(pluginRemoteSync);
PowerMock.mockStaticPartial(PluginRemoteSync.class, "getInstance");
EasyMock.expect(PluginRemoteSync.getInstance()).andReturn(pluginRemoteSync).anyTimes();
PowerMock.replay(PluginRemoteSync.class);
// 本地插件模拟检查
TemplateIOErrorContextHolder.registerPluginNameMap(new HashMap<String, String>() {{
put("com.fr.plugin1", "好用的插件1");
put("com.fr.plugin2", "好用的插件2");
put("com.fr.plugin3", "好用的插件3");
put("com.fr.plugin4", "好用的插件4");
put("com.fr.plugin5", "好用的插件5");
}},new HashSet<>());
// unknown
TemplateIOErrorContextHolder.addNeedInstallPlugin(PluginMarker.create("com.fr.plugin7", "1"));
// disable
TemplateIOErrorContextHolder.addNeedEnablePlugin(PluginMarkerAdapter.create("com.fr.plugin5", "1", "plugin5"));
TemplateIOErrorContextHolder.addNeedInstallPlugin(PluginMarker.create("com.fr.plugin3", "1"));
// not install
TemplateIOErrorContextHolder.addNeedInstallPlugin(PluginMarker.create("com.fr.plugin1", "1"));
TemplateIOErrorContextHolder.addNeedInstallPlugin(PluginMarker.create("com.fr.plugin4", "1"));
Multimap<String, PluginMarkerAdapter> pendingPlugins = TemplateIOErrorContextHolder.getPendingPlugin();
Reflect.on(DesignerAppUtils.class).call("rearrange",pendingPlugins).get();
Assert.assertEquals(1,pendingPlugins.get(TemplateIOErrorContextHolder.UNKNOWN_PLUGIN).size());
Collection<PluginMarkerAdapter> pluginMarkerAdapters = pendingPlugins.get(TemplateIOErrorContextHolder.DISABLE_PLUGIN);
Assert.assertEquals(2, pluginMarkerAdapters.size());
pluginMarkerAdapters.contains(PluginMarker.create("com.fr.plugin3", "1"));
pluginMarkerAdapters.contains(PluginMarker.create("com.fr.plugin4", "1"));
Collection<PluginMarkerAdapter> pluginMarkerAdapters1 = pendingPlugins.get(TemplateIOErrorContextHolder.NOT_INSTALLED_PLUGIN);
Assert.assertEquals(1, pluginMarkerAdapters1.size());
pluginMarkerAdapters1.contains(PluginMarker.create("com.fr.plugin5","1"));
}
}
Loading…
Cancel
Save