Browse Source

KERNEL-6380 在部分插件切换的时候,需要有一个集中刷新缓存的地方

feature/big-screen
hades 4 years ago
parent
commit
1bfb44b088
  1. 174
      designer-base/src/main/java/com/fr/base/ClassHelper.java
  2. 51
      designer-base/src/main/java/com/fr/design/PluginRefreshManager.java
  3. 2
      designer-base/src/main/java/com/fr/design/data/datapane/TableDataTreePane.java
  4. 97
      designer-base/src/main/java/com/fr/design/file/HistoryTemplateListCache.java
  5. 2
      designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java
  6. 28
      designer-base/src/test/java/com/fr/base/ClassHelperTest.java

174
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<String> primClasses = new HashSet<>();
private static final Set<ClassLoader> classLoaders = new HashSet<>();
private static final Map<String, Field[]> map = new HashMap<>();
private static final Set<Object> 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<ClassLoader> getPluginClassLoaders(Object o) {
try {
collectClassloader(o);
Set<ClassLoader> 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<Field> 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]);
}
}

51
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);
}
}

2
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();

97
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<JTemplate<?, ?>> historyList;
private JTemplate<?, ?> editingTemplate;
private Map<String, Boolean> 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<ClassLoader> 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);
}
}
}
}
}

2
designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java

@ -344,12 +344,10 @@ public abstract class JTemplate<T extends BaseBook, U extends BaseUndoState<?>>
this.template = JTemplateFactory.asIOFile(file);
setTarget(this.template);
UIUtil.invokeLaterIfNeeded(() -> {
// 先移除旧的。
removeCenterPane();
// 加入新的
addCenterPane();
});
} catch (Exception e) {
FineLoggerFactory.getLogger().error(e.getMessage(), e);
}

28
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<ClassLoader> set = ClassHelper.getPluginClassLoaders(workBook);
Assert.assertEquals(0, set.size());
Form form = new Form();
Set<ClassLoader> set1 = ClassHelper.getPluginClassLoaders(form);
Assert.assertEquals(0, set1.size());
}
}
Loading…
Cancel
Save