From 1bfb44b088ef206f140a2cfcc053cceabc156dad Mon Sep 17 00:00:00 2001 From: hades Date: Tue, 29 Dec 2020 09:25:37 +0800 Subject: [PATCH] =?UTF-8?q?KERNEL-6380=20=E5=9C=A8=E9=83=A8=E5=88=86?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E5=88=87=E6=8D=A2=E7=9A=84=E6=97=B6=E5=80=99?= =?UTF-8?q?=EF=BC=8C=E9=9C=80=E8=A6=81=E6=9C=89=E4=B8=80=E4=B8=AA=E9=9B=86?= =?UTF-8?q?=E4=B8=AD=E5=88=B7=E6=96=B0=E7=BC=93=E5=AD=98=E7=9A=84=E5=9C=B0?= =?UTF-8?q?=E6=96=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/fr/base/ClassHelper.java | 174 ++++++++++++++++++ .../com/fr/design/PluginRefreshManager.java | 51 +++-- .../data/datapane/TableDataTreePane.java | 2 + .../design/file/HistoryTemplateListCache.java | 97 +++++----- .../com/fr/design/mainframe/JTemplate.java | 2 - .../java/com/fr/base/ClassHelperTest.java | 28 +++ 6 files changed, 300 insertions(+), 54 deletions(-) create mode 100644 designer-base/src/main/java/com/fr/base/ClassHelper.java create mode 100644 designer-base/src/test/java/com/fr/base/ClassHelperTest.java diff --git a/designer-base/src/main/java/com/fr/base/ClassHelper.java b/designer-base/src/main/java/com/fr/base/ClassHelper.java new file mode 100644 index 000000000..00ff7f9eb --- /dev/null +++ b/designer-base/src/main/java/com/fr/base/ClassHelper.java @@ -0,0 +1,174 @@ +package com.fr.base; + +import com.fr.general.ComparatorUtils; +import com.fr.log.FineLoggerFactory; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.jetbrains.annotations.Nullable; + +/** + * @author hades + * @version 10.0 + * Created by hades on 2020/12/28 + */ +public class ClassHelper { + + private static final Set primClasses = new HashSet<>(); + + private static final Set classLoaders = new HashSet<>(); + + private static final Map map = new HashMap<>(); + + private static final Set data = new HashSet<>(); + + private static final String PLUGIN_CLASS_CLASSLOADER= "com.fr.plugin.engine.core.PluginClassLoader"; + + static { + primClasses.add("boolean"); + primClasses.add("byte"); + primClasses.add("char"); + primClasses.add("short"); + primClasses.add("int"); + primClasses.add("long"); + primClasses.add("float"); + primClasses.add("double"); + primClasses.add("void"); + primClasses.add("java.lang.Boolean"); + primClasses.add("java.lang.Byte"); + primClasses.add("java.lang.Character"); + primClasses.add("java.lang.Short"); + primClasses.add("java.lang.Integer"); + primClasses.add("java.lang.Long"); + primClasses.add("java.lang.FLOAT"); + primClasses.add("java.lang.DOUBLE"); + primClasses.add("java.lang.Void"); + primClasses.add("java.lang.String"); + primClasses.add("java.awt.image.BufferedImage"); + } + + + /** + * 获取当前对象字段中由插件加载出类的classloader + * + * @param o + * @return + */ + @Nullable + public static Set getPluginClassLoaders(Object o) { + try { + collectClassloader(o); + Set result = new HashSet<>(classLoaders); + data.clear(); + classLoaders.clear(); + map.clear(); + return result; + } catch (Throwable e) { + FineLoggerFactory.getLogger().warn(e.getMessage(), e); + data.clear(); + classLoaders.clear(); + map.clear(); + return null; + } + } + + private static void collectClassloader(Object o) { + if (o == null) { + return; + } + Field[] fields; + Class aclass = o.getClass(); + + String className = aclass.getName(); + + if (map.containsKey(className)) { + fields = map.get(className); + } else { + fields = fields(aclass, o); + map.put(className, fields); + } + + if (aclass.isArray()) { + for (int i = 0, len = Array.getLength(o); i < len; i++) { + Object arrayOb = Array.get(o, i); + if (arrayOb != null) { + Class arrayObClass = arrayOb.getClass(); + String name = arrayObClass.getName(); + if (primClasses.contains(name)) { + return; + } + data.add(arrayOb); + ClassLoader loader = arrayObClass.getClassLoader(); + if (loader != null && ComparatorUtils.equals(loader.getClass().getName(), PLUGIN_CLASS_CLASSLOADER)) { + classLoaders.add(loader); + } + } else { + continue; + } + collectClassloader(arrayOb); + } + } + + if (fields.length == 0) { + return; + } + + for (Field field : fields) { + if (!field.isAccessible()) { + field.setAccessible(true); + } + Object ob = null; + try { + ob = field.get(o); + + if (ob == null) { + continue; + } + Class clazz = ob.getClass(); + String name = clazz.getName(); + + if (primClasses.contains(name)) { + continue; + } + + if (!data.contains(ob)) { + data.add(ob); + ClassLoader loader = clazz.getClassLoader(); + if (loader != null && ComparatorUtils.equals(loader.getClass().getName(), PLUGIN_CLASS_CLASSLOADER)) { + classLoaders.add(loader); + } + collectClassloader(ob); + } + + } catch (Throwable e) { + FineLoggerFactory.getLogger().warn(e.getMessage(), e); + } + } + } + + + + private static Field[] fields(Class clazz, Object o) { + + Class t = clazz; + Set result = new HashSet<>(); + + do { + for (Field field : t.getDeclaredFields()) { + if (clazz != o ^ Modifier.isStatic(field.getModifiers())) { + result.add(field); + } + } + + t = t.getSuperclass(); + } + while (t != null); + + return result.toArray(new Field[0]); + } + +} diff --git a/designer-base/src/main/java/com/fr/design/PluginRefreshManager.java b/designer-base/src/main/java/com/fr/design/PluginRefreshManager.java index 67442bb06..6ccf1f190 100644 --- a/designer-base/src/main/java/com/fr/design/PluginRefreshManager.java +++ b/designer-base/src/main/java/com/fr/design/PluginRefreshManager.java @@ -1,14 +1,13 @@ package com.fr.design; +import com.fr.design.constants.DesignerLaunchStatus; import com.fr.design.file.HistoryTemplateListCache; import com.fr.design.fun.HyperlinkProvider; import com.fr.design.fun.TableDataDefineProvider; -import com.fr.plugin.manage.PluginFilter; import com.fr.plugin.observer.PluginEvent; import com.fr.plugin.observer.PluginEventListener; import com.fr.plugin.observer.PluginEventType; import com.fr.plugin.observer.PluginListenerRegistration; -import com.fr.stable.fun.IOFileAttrMark; import java.util.HashSet; import java.util.Set; @@ -28,18 +27,33 @@ public class PluginRefreshManager { private final PluginEventListener pluginAfterRunEventListener = new PluginEventListener() { @Override public void on(PluginEvent event) { + boolean mustReload = false; + // 兼容之前版本特性 + for (String tag : context) { + if (event.getContext().contain(tag)) { + mustReload = true; + break; + } + } // 重新载入模板xml内容 到 Workbook/Form对象中 - HistoryTemplateListCache.getInstance().reloadAllEditingTemplate(); + HistoryTemplateListCache.getInstance().reloadAllEditingTemplate(mustReload); } }; - private final PluginFilter pluginFilter = pluginContext -> { - for (String xmlTag : context) { - if (pluginContext.contain(xmlTag)) { - return true; + private final PluginEventListener pluginBeforeStopEventListener = new PluginEventListener() { + @Override + public void on(PluginEvent event) { + boolean canCal = true; + for (String tag : context) { + if (event.getContext().contain(tag)) { + canCal = false; + break; + } + } + if (canCal) { + HistoryTemplateListCache.getInstance().calNeedReloadTemplate(event.getContext()); } } - return false; }; @@ -48,13 +62,26 @@ public class PluginRefreshManager { } public void load() { - context.add(IOFileAttrMark.MARK_STRING); context.add(TableDataDefineProvider.XML_TAG); context.add(HyperlinkProvider.XML_TAG); } private PluginRefreshManager() { - PluginListenerRegistration.getInstance().listen(PluginEventType.AfterRun, this.pluginAfterRunEventListener, this.pluginFilter); + PluginListenerRegistration.getInstance().listen(PluginEventType.BeforeAllActive, new PluginEventListener() { + @Override + public void on(PluginEvent event) { + removePluginListener(); + } + }); + PluginListenerRegistration.getInstance().listen(PluginEventType.AfterAllActive, new PluginEventListener() { + @Override + public void on(PluginEvent event) { + addPluginListener(); + if (DesignerLaunchStatus.getStatus() != DesignerLaunchStatus.WORKSPACE_INIT_COMPLETE) { + HistoryTemplateListCache.getInstance().reloadAllEditingTemplate(true); + } + } + }); } public void registerItem(String xmlTag) { @@ -63,11 +90,13 @@ public class PluginRefreshManager { public void removePluginListener() { PluginListenerRegistration.getInstance().stopListen(this.pluginAfterRunEventListener); + PluginListenerRegistration.getInstance().stopListen(this.pluginBeforeStopEventListener); } public void addPluginListener() { - PluginListenerRegistration.getInstance().listen(PluginEventType.AfterRun, this.pluginAfterRunEventListener, this.pluginFilter); + PluginListenerRegistration.getInstance().listen(PluginEventType.AfterRun, this.pluginAfterRunEventListener); + PluginListenerRegistration.getInstance().listen(PluginEventType.BeforeStop, this.pluginBeforeStopEventListener); } } diff --git a/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java b/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java index f1a3cf336..ca587c3da 100644 --- a/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java +++ b/designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java @@ -94,6 +94,8 @@ public class TableDataTreePane extends BasicTableDataTreePane { addMenuDef.setIconPath(IconPathConstants.ADD_POPMENU_ICON_PATH); createAddMenuDef(); + // 创建插件监听 + createPluginListener(); editAction = new EditAction(); removeAction = new RemoveAction(); diff --git a/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java b/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java index 05b2a24f6..242d1d5c1 100644 --- a/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java +++ b/designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java @@ -1,5 +1,6 @@ package com.fr.design.file; +import com.fr.base.ClassHelper; import com.fr.base.chart.chartdata.CallbackEvent; import com.fr.base.io.BaseBook; import com.fr.design.DesignerEnvManager; @@ -18,6 +19,8 @@ import com.fr.file.FileNodeFILE; import com.fr.file.StashedFILE; import com.fr.general.ComparatorUtils; import com.fr.log.FineLoggerFactory; +import com.fr.plugin.context.PluginContext; +import com.fr.plugin.manage.PluginManager; import com.fr.stable.CoreConstants; import com.fr.stable.StringUtils; import com.fr.third.org.apache.commons.io.FilenameUtils; @@ -29,6 +32,7 @@ import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.Set; /** * 历史模板缓存 @@ -42,6 +46,8 @@ public class HistoryTemplateListCache implements CallbackEvent { private List> historyList; private JTemplate editingTemplate; + private Map templateNeedReloadMap = new HashMap<>(); + public static HistoryTemplateListCache getInstance() { return Holder.INSTANCE; } @@ -72,6 +78,9 @@ public class HistoryTemplateListCache implements CallbackEvent { selected.fireJTemplateClosed(); selected.stopEditing(); try { + if (!templateNeedReloadMap.isEmpty()) { + templateNeedReloadMap.remove(selected.getEditingFILE().getName()); + } historyList.remove(contains(selected)); selected.getEditingFILE().closeTemplate(); FineLoggerFactory.getLogger().info(Toolkit.i18nText("Fine-Design_Basic_Template_Closed_Warn_Text", selected.getEditingFILE().getName())); @@ -370,7 +379,6 @@ public class HistoryTemplateListCache implements CallbackEvent { */ public void load() { FineLoggerFactory.getLogger().info("Env Change Template Loading..."); - JTemplate currentTemplate = null; if (stashFILEMap != null && stashFILEMap.size() != 0) { int size = historyList.size(); for (int i = 0; i < size; i++) { @@ -381,32 +389,14 @@ public class HistoryTemplateListCache implements CallbackEvent { if (stashedFile == null) { continue; } - JTemplate template = JTemplateFactory.createJTemplate(stashedFile); - if (template != null) { - historyList.set(i, template); - // 替换当前正在编辑的模板,使用添加并激活的方式,以便使用统一的入口来处理监听事件 - if (isCurrentEditingFile(template.getPath())) { - currentTemplate = template; - } - } else { - // 当模板为空时 说明是一个新建的未保存模板 但是内存中保存了该模板 可以从中获取 - JTemplate jt = historyList.get(i); - // 另外如果该模板是正在编辑的模板,需要要激活 - if (jt != null && isCurrentEditingFile(jt.getPath())) { - currentTemplate = jt; - } - } + FineLoggerFactory.getLogger().info("{} is being reloaded", stashedFile.getName()); + JTemplate template = historyList.get(i); + template.refreshResource(stashedFile); } catch (Exception e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } } - // 最后加载当前正在编辑的模板 以保证数据集刷新正常 - if (currentTemplate != null) { - loadCurrentTemplate(currentTemplate); - } stashFILEMap.clear(); - MutilTempalteTabPane.getInstance().refreshOpenedTemplate(historyList); - MutilTempalteTabPane.getInstance().repaint(); } FineLoggerFactory.getLogger().info("Env Change Template Loaded."); } @@ -422,11 +412,7 @@ public class HistoryTemplateListCache implements CallbackEvent { */ @Deprecated public void reloadCurrentTemplate() { - JTemplate jt = getCurrentEditingTemplate(); - boolean access = jt != null && jt.getEditingFILE() != null && jt.getEditingFILE().exists(); - if (access) { - jt.refreshResource(); - } + reloadAllEditingTemplate(true); } /** @@ -444,27 +430,56 @@ public class HistoryTemplateListCache implements CallbackEvent { /** * 插件安装后/启用 重新加载模板资源 */ - public void reloadAllEditingTemplate() { - FineLoggerFactory.getLogger().info("Plugin env Change reload all template started"); + public void reloadAllEditingTemplate(boolean mustReload) { + FineLoggerFactory.getLogger().info("Plugin env change reload all template started"); + long start = System.currentTimeMillis(); int size = historyList.size(); for (int i = 0; i < size; i++) { JTemplate template = historyList.get(i); FILE file = template.getEditingFILE(); - try { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - BaseBook target = template.getTarget(); - if (target != null) { - FineLoggerFactory.getLogger().info("{} is being reloaded", template.getName()); - target.export(outputStream); - FILE stashedFile = new StashedFILE(file, outputStream.toByteArray()); - template.refreshResource(stashedFile); + Boolean value = templateNeedReloadMap.get(file.getName()); + boolean needReload = mustReload || (value != null && value); + if (needReload) { + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + BaseBook target = template.getTarget(); + if (target != null) { + FineLoggerFactory.getLogger().info("{} is being reloaded", file.getName()); + target.export(outputStream); + FILE stashedFile = new StashedFILE(file, outputStream.toByteArray()); + template.refreshResource(stashedFile); + } + // 如果 target == null 那么这个模板是被模板内存优化功能处理过的,不用处理 + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); } - // 如果 target == null 那么这个模板是被模板内存优化功能处理过的,不用处理 - } catch (Exception e) { - FineLoggerFactory.getLogger().error(e.getMessage(), e); } } - FineLoggerFactory.getLogger().info("Plugin env Change reload all template ended"); + templateNeedReloadMap.clear(); + FineLoggerFactory.getLogger().info("Plugin env change reload all template ended"); + FineLoggerFactory.getLogger().debug("Reload all template spend: {} ms", (System.currentTimeMillis() - start)); + } + + public void calNeedReloadTemplate(PluginContext context) { + for (JTemplate template : historyList) { + BaseBook baseBook = template.getTarget(); + if (baseBook != null) { + String name = template.getEditingFILE().getName(); + long start = System.currentTimeMillis(); + Set set = ClassHelper.getPluginClassLoaders(baseBook); + FineLoggerFactory.getLogger().info("{} find plugin classloader spend: {} ms", name, (System.currentTimeMillis() - start)); + if (set != null) { + for (ClassLoader classLoader : set) { + PluginContext pluginContext = PluginManager.getContext(classLoader); + if (pluginContext != null && ComparatorUtils.equals(context.getName(), pluginContext.getName())) { + templateNeedReloadMap.put(name, true); + } + } + } else { + templateNeedReloadMap.put(name, true); + } + } + } } } diff --git a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java index 87bcd23f5..d80d9f07a 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java @@ -344,12 +344,10 @@ public abstract class JTemplate> this.template = JTemplateFactory.asIOFile(file); setTarget(this.template); - UIUtil.invokeLaterIfNeeded(() -> { // 先移除旧的。 removeCenterPane(); // 加入新的 addCenterPane(); - }); } catch (Exception e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } diff --git a/designer-base/src/test/java/com/fr/base/ClassHelperTest.java b/designer-base/src/test/java/com/fr/base/ClassHelperTest.java new file mode 100644 index 000000000..ae0facfcf --- /dev/null +++ b/designer-base/src/test/java/com/fr/base/ClassHelperTest.java @@ -0,0 +1,28 @@ +package com.fr.base; + +import com.fr.form.main.Form; +import com.fr.main.impl.WorkBook; +import java.util.Set; +import junit.framework.TestCase; +import org.junit.Assert; + + +/** + * @author hades + * @version 10.0 + * Created by hades on 2020/12/28 + */ +public class ClassHelperTest extends TestCase { + + + public void testGetPluginClassLoaders() { + WorkBook workBook = new WorkBook(); + Set set = ClassHelper.getPluginClassLoaders(workBook); + Assert.assertEquals(0, set.size()); + Form form = new Form(); + Set set1 = ClassHelper.getPluginClassLoaders(form); + Assert.assertEquals(0, set1.size()); + + } + +} \ No newline at end of file