diff --git a/plugin.xml b/plugin.xml
index b1c65ad..6dc3add 100755
--- a/plugin.xml
+++ b/plugin.xml
@@ -3,12 +3,13 @@
com.fr.solution.plugin.db.redis.v10
yes
- 4.2
+ 5.0
10.0
2018-11-29
richie
[2019-02-15]结果集可以通过JavaScript脚本进行转换。
[2018-11-30]解决远程设计时无法查询key列表的问题。
[2018-11-26]解决10.0版本中服务器数据集无法保存的问题。
@@ -25,6 +26,9 @@
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/db/redis/RedisScriptTableDataDefine.java b/src/main/java/com/fr/plugin/db/redis/RedisScriptTableDataDefine.java
new file mode 100644
index 0000000..52f73fc
--- /dev/null
+++ b/src/main/java/com/fr/plugin/db/redis/RedisScriptTableDataDefine.java
@@ -0,0 +1,52 @@
+package com.fr.plugin.db.redis;
+
+import com.fr.base.TableData;
+import com.fr.design.data.tabledata.tabledatapane.AbstractTableDataPane;
+import com.fr.design.fun.ServerTableDataDefineProvider;
+import com.fr.design.fun.impl.AbstractTableDataDefineProvider;
+import com.fr.locale.InterProviderFactory;
+import com.fr.plugin.db.redis.core.RedisScriptTableData;
+import com.fr.plugin.db.redis.ui.RedisScriptTableDataPane;
+
+/**
+ * @author richie
+ * @version 10.0
+ * Created by richie on 2019-03-18
+ */
+public class RedisScriptTableDataDefine extends AbstractTableDataDefineProvider implements ServerTableDataDefineProvider {
+
+ @Override
+ public int currentAPILevel() {
+ return CURRENT_LEVEL;
+ }
+
+ @Override
+ public Class extends TableData> classForTableData() {
+ return RedisScriptTableData.class;
+ }
+
+ @Override
+ public Class extends TableData> classForInitTableData() {
+ return RedisScriptTableData.class;
+ }
+
+ @Override
+ public Class extends AbstractTableDataPane> appearanceForTableData() {
+ return RedisScriptTableDataPane.class;
+ }
+
+ @Override
+ public String nameForTableData() {
+ return InterProviderFactory.getProvider().getLocText("Plugin-Redis_Script_Table_Data");
+ }
+
+ @Override
+ public String prefixForTableData() {
+ return "redis";
+ }
+
+ @Override
+ public String iconPathForTableData() {
+ return "/com/fr/plugin/db/redis/images/redis.png";
+ }
+}
diff --git a/src/main/java/com/fr/plugin/db/redis/core/RedisDatabaseConnection.java b/src/main/java/com/fr/plugin/db/redis/core/RedisDatabaseConnection.java
index 299c739..6c931f7 100755
--- a/src/main/java/com/fr/plugin/db/redis/core/RedisDatabaseConnection.java
+++ b/src/main/java/com/fr/plugin/db/redis/core/RedisDatabaseConnection.java
@@ -4,7 +4,6 @@ import com.fr.config.holder.Conf;
import com.fr.config.holder.factory.Holders;
import com.fr.data.impl.AbstractDatabaseConnection;
import com.fr.data.impl.Connection;
-import com.fr.general.Inter;
import com.fr.locale.InterProviderFactory;
import com.fr.security.SecurityToolbox;
import com.fr.stable.ArrayUtils;
@@ -12,7 +11,7 @@ import com.fr.stable.StableUtils;
import com.fr.stable.StringUtils;
import com.fr.stable.xml.XMLPrintWriter;
import com.fr.stable.xml.XMLableReader;
-import redis.clients.jedis.Jedis;
+import com.fr.third.redis.clients.jedis.Jedis;
import java.util.List;
import java.util.Set;
@@ -20,10 +19,12 @@ import java.util.Set;
public class RedisDatabaseConnection extends AbstractDatabaseConnection {
private static final int DEFAULT_REDIS_PORT = 6379;
+ private static final int MAX_TOTAL = 10;
private Conf host = Holders.simple(StringUtils.EMPTY);
private Conf port = Holders.simple(DEFAULT_REDIS_PORT);
private Conf password = Holders.simple(StringUtils.EMPTY);
+ private Conf maxTotal = Holders.simple(MAX_TOTAL);
public RedisDatabaseConnection() {
@@ -53,7 +54,13 @@ public class RedisDatabaseConnection extends AbstractDatabaseConnection {
this.password.set(password);
}
+ public int getMaxTotal() {
+ return maxTotal.get();
+ }
+ public void setMaxTotal(Integer maxTotal) {
+ this.maxTotal.set(maxTotal);
+ }
@Override
public void testConnection() throws Exception {
@@ -70,11 +77,7 @@ public class RedisDatabaseConnection extends AbstractDatabaseConnection {
}
public Jedis createRedisClient() {
- Jedis client = new Jedis(getHost(), getPort());
- if (StringUtils.isNotEmpty(getPassword())) {
- client.auth(getPassword());
- }
- return client;
+ return RedisPool.getPool().getResource(getHost(), getPort(), getPassword(), getMaxTotal());
}
@Override
@@ -148,6 +151,7 @@ public class RedisDatabaseConnection extends AbstractDatabaseConnection {
if (StringUtils.isNotEmpty(pwd)) {
setPassword(SecurityToolbox.encrypt(pwd));
}
+ setMaxTotal(reader.getAttrAsInt("maxTotal", MAX_TOTAL));
}
}
}
@@ -161,6 +165,7 @@ public class RedisDatabaseConnection extends AbstractDatabaseConnection {
if (StringUtils.isNotEmpty(getPassword())) {
writer.attr("password", SecurityToolbox.decrypt(getPassword()));
}
+ writer.attr("maxTotal", getMaxTotal());
writer.end();
}
@@ -170,6 +175,7 @@ public class RedisDatabaseConnection extends AbstractDatabaseConnection {
cloned.host = (Conf) host.clone();
cloned.port = (Conf) port.clone();
cloned.password = (Conf) password.clone();
+ cloned.maxTotal = (Conf) maxTotal.clone();
return cloned;
}
}
\ No newline at end of file
diff --git a/src/main/java/com/fr/plugin/db/redis/core/RedisPool.java b/src/main/java/com/fr/plugin/db/redis/core/RedisPool.java
new file mode 100644
index 0000000..a12daa9
--- /dev/null
+++ b/src/main/java/com/fr/plugin/db/redis/core/RedisPool.java
@@ -0,0 +1,75 @@
+package com.fr.plugin.db.redis.core;
+
+import com.fr.stable.StringUtils;
+import com.fr.third.redis.clients.jedis.Jedis;
+import com.fr.third.redis.clients.jedis.JedisPool;
+import com.fr.third.redis.clients.jedis.JedisPoolConfig;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author richie
+ * @version 10.0
+ * Created by richie on 2019-03-18
+ */
+public class RedisPool {
+
+ private static final int TIME_OUT = 100 * 1000;
+
+ private static RedisPool pool = new RedisPool();
+
+ public static RedisPool getPool() {
+ return pool;
+ }
+
+ private Map jedisPoolMap = new ConcurrentHashMap();
+
+ public Jedis getResource(String host, int port, String password, int maxTotal) {
+ JedisPool jedisPool = jedisPoolMap.get(host);
+ if (jedisPool == null) {
+ jedisPool = createJedisPool(host, port, password, maxTotal);
+ jedisPoolMap.put(host, jedisPool);
+ }
+ return jedisPool.getResource();
+ }
+
+ private JedisPool createJedisPool(String host, int port, String password, int maxTotal) {
+ JedisPoolConfig config = new JedisPoolConfig();
+ config.setMaxTotal(maxTotal);
+ //连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
+ config.setBlockWhenExhausted(true);
+ //设置的逐出策略类名, 默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数)
+ config.setEvictionPolicyClassName("com.fr.third.org.apache.commons.pool2.impl.DefaultEvictionPolicy");
+ //是否启用pool的jmx管理功能, 默认true
+ config.setJmxEnabled(true);
+ //MBean ObjectName = new ObjectName("org.apache.commons.pool2:type=GenericObjectPool,name=" + "pool" + i); 默认为"pool", JMX不熟,具体不知道是干啥的...默认就好.
+ config.setJmxNamePrefix("pool");
+ //是否启用后进先出, 默认true
+ config.setLifo(true);
+ //最大空闲连接数
+ config.setMaxIdle(10);
+ //最大连接数,可配置
+ //获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1
+ config.setMaxWaitMillis(-1);
+ //逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
+ config.setMinEvictableIdleTimeMillis(1800000);
+ //最小空闲连接数, 默认0
+ config.setMinIdle(0);
+ //每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
+ config.setNumTestsPerEvictionRun(3);
+ //对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断 (默认逐出策略)
+ config.setSoftMinEvictableIdleTimeMillis(1800000);
+ //在获取连接的时候检查有效性, 默认false
+ config.setTestOnBorrow(false);
+ //在空闲时检查有效性, 默认false
+ config.setTestWhileIdle(false);
+ //逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
+ config.setTimeBetweenEvictionRunsMillis(-1);
+ if (StringUtils.isNotBlank(password)) {
+ return new JedisPool(config, host, port, TIME_OUT, password);
+ } else {
+ return new JedisPool(config, host, port, TIME_OUT);
+ }
+ }
+}
diff --git a/src/main/java/com/fr/plugin/db/redis/core/RedisScriptTableData.java b/src/main/java/com/fr/plugin/db/redis/core/RedisScriptTableData.java
new file mode 100644
index 0000000..25befc0
--- /dev/null
+++ b/src/main/java/com/fr/plugin/db/redis/core/RedisScriptTableData.java
@@ -0,0 +1,144 @@
+package com.fr.plugin.db.redis.core;
+
+import com.fr.base.Parameter;
+import com.fr.base.TableData;
+import com.fr.config.holder.Conf;
+import com.fr.config.holder.factory.Holders;
+import com.fr.config.holder.factory.XmlHolders;
+import com.fr.data.AbstractParameterTableData;
+import com.fr.data.core.DataCoreXmlUtils;
+import com.fr.data.impl.Connection;
+import com.fr.data.impl.NameDatabaseConnection;
+import com.fr.file.DatasourceManager;
+import com.fr.general.data.DataModel;
+import com.fr.general.xml.GeneralXMLTools;
+import com.fr.intelli.record.Focus;
+import com.fr.log.FineLoggerFactory;
+import com.fr.plugin.db.redis.core.order.OrderValue;
+import com.fr.plugin.db.redis.core.order.impl.NumberOrderValue;
+import com.fr.plugin.db.redis.util.RedisUtils;
+import com.fr.record.analyzer.EnableMetrics;
+import com.fr.script.Calculator;
+import com.fr.stable.ParameterProvider;
+import com.fr.stable.StringUtils;
+import com.fr.stable.xml.XMLPrintWriter;
+import com.fr.stable.xml.XMLableReader;
+
+/**
+ * @author richie
+ * @version 10.0
+ * Created by richie on 2019-03-18
+ */
+@EnableMetrics
+public class RedisScriptTableData extends AbstractParameterTableData {
+
+ private static final long serialVersionUID = 1525853354993816818L;
+
+ private Conf database = Holders.obj(null, Connection.class);
+ private Conf dbIndex = XmlHolders.obj(new NumberOrderValue(0), OrderValue.class, OrderValue.XML_TAG);
+ private Conf script = Holders.simple(StringUtils.EMPTY);
+
+ public void setDatabase(Connection c) {
+ this.database.set(c);
+ }
+
+ public Connection getDatabase() {
+ return database.get();
+ }
+
+ public OrderValue getOrderValue() {
+ return dbIndex.get();
+ }
+
+ public void setOrderValue(OrderValue dbIndex) {
+ this.dbIndex.set(dbIndex);
+ }
+
+ public String getScript() {
+ return script.get();
+ }
+
+ public void setScript(String script) {
+ this.script.set(script);
+ }
+
+ public void setParameters(ParameterProvider[] providers) {
+ super.setDefaultParameters(providers);
+ }
+
+ @Override
+ public DataModel createDataModel(Calculator calculator) {
+ return createDataModel(calculator, TableData.RESULT_ALL);
+ }
+
+ @Override
+ @Focus(id = RedisConstants.PLUGIN_ID, text = "Plugin-Redis_Script_Table_Data")
+ public DataModel createDataModel(Calculator calculator, int rowCount) {
+ long start = System.currentTimeMillis();
+ Parameter[] ps = Parameter.providers2Parameter(getParameters(calculator));
+ Connection connection = database.get();
+ if (connection instanceof NameDatabaseConnection) {
+ String name = ((NameDatabaseConnection) connection).getName();
+ RedisDatabaseConnection rc = DatasourceManager.getProviderInstance().getConnection(name, RedisDatabaseConnection.class);
+ if (rc != null) {
+ OrderValue orderValue = dbIndex.get();
+ DataModel model = new RedisScriptTableDataModel(rc,
+ orderValue == null ? 0 : orderValue.toIndex(calculator, ps),
+ RedisUtils.calculateQuery(script.get(), ps),
+ rowCount);
+ FineLoggerFactory.getLogger().info("Build data model spend time {} ms.", System.currentTimeMillis() - start);
+ return model;
+ }
+ }
+ return null;
+ }
+
+ public void readXML(XMLableReader reader) {
+ super.readXML(reader);
+
+ if (reader.isChildNode()) {
+ String tmpName = reader.getTagName();
+ String tmpVal;
+
+ if (OrderValue.XML_TAG.equals(tmpName)) {
+ OrderValue orderValue = (OrderValue) GeneralXMLTools.readXMLable(reader);
+ if (orderValue != null) {
+ setOrderValue(orderValue);
+ }
+ } else if (com.fr.data.impl.Connection.XML_TAG.equals(tmpName)) {
+ if (reader.getAttrAsString("class", null) != null) {
+ com.fr.data.impl.Connection con = DataCoreXmlUtils.readXMLConnection(reader);
+ this.setDatabase(con);
+ }
+ } else if ("Script".equals(tmpName)) {
+ tmpVal = reader.getElementValue();
+ if (isNotNullValue(tmpVal)) {
+ this.setScript(tmpVal);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void writeXML(XMLPrintWriter writer) {
+ super.writeXML(writer);
+ GeneralXMLTools.writeXMLable(writer, dbIndex.get(), OrderValue.XML_TAG);
+ if (this.database.get() != null) {
+ DataCoreXmlUtils.writeXMLConnection(writer, this.database.get());
+ }
+ writer.startTAG("Script").textNode(getScript()).end();
+ }
+
+ private boolean isNotNullValue(String value) {
+ return value != null && !"null".equals(value);
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ RedisScriptTableData cloned = (RedisScriptTableData) super.clone();
+ cloned.database = (Conf) database.clone();
+ cloned.script = (Conf) script.clone();
+ cloned.dbIndex = (Conf) dbIndex.clone();
+ return cloned;
+ }
+}
diff --git a/src/main/java/com/fr/plugin/db/redis/core/RedisScriptTableDataModel.java b/src/main/java/com/fr/plugin/db/redis/core/RedisScriptTableDataModel.java
new file mode 100644
index 0000000..f8f3c09
--- /dev/null
+++ b/src/main/java/com/fr/plugin/db/redis/core/RedisScriptTableDataModel.java
@@ -0,0 +1,141 @@
+package com.fr.plugin.db.redis.core;
+
+import com.eclipsesource.v8.V8;
+import com.eclipsesource.v8.V8Array;
+import com.eclipsesource.v8.V8Object;
+import com.fr.data.AbstractDataModel;
+import com.fr.plugin.db.redis.help.Console;
+import com.fr.plugin.db.redis.help.Files;
+import com.fr.plugin.db.redis.help.RedisClient;
+import com.fr.plugin.db.redis.util.RedisUtils;
+import com.fr.third.redis.clients.jedis.Jedis;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author richie
+ * @version 10.0
+ * Created by richie on 2019-03-18
+ */
+public class RedisScriptTableDataModel extends AbstractDataModel {
+
+ private RedisDatabaseConnection mc;
+ private String script;
+ private int dbIndex;
+ private int rowCount;
+ private List> data = new ArrayList>();
+ private String[] columnNames = null;
+
+ public RedisScriptTableDataModel(RedisDatabaseConnection mc, int dbIndex, String script, int rowCount) {
+ this.mc = mc;
+ this.script = script;
+ this.dbIndex = dbIndex;
+ this.rowCount = rowCount;
+ }
+
+ private void buildData() {
+ if (columnNames != null) {
+ return;
+ }
+ Jedis jedis = mc.createRedisClient();
+ jedis.select(dbIndex);
+ V8 v8 = V8.createV8Runtime();
+ RedisClient client = new RedisClient(v8, jedis);
+ V8Object v8Console = initConsole(v8);
+ V8Object v8Require = initRequire(v8);
+ V8Object v8Client = initRedis(v8, client);
+
+ v8.executeVoidScript(
+ "function unique(array) {return Array.from(new Set(array));};" +
+ "function merge(table, column) {return {content:table, column:column}};");
+
+ V8Object returnObj = v8.executeObjectScript(String.format("(function(){%s})();", script));
+
+ V8Array v8Column = returnObj.getArray("column");
+ V8Array v8Table = returnObj.getArray("content");
+ int columnCount = v8Column.length();
+ columnNames = new String[columnCount];
+ for (int i = 0; i < columnCount; i++) {
+ columnNames[i] = String.valueOf(v8Column.get(i));
+ }
+ int returnRowCount = v8Table.length();
+ for (int i = 0, realCount = Math.min(returnRowCount, this.rowCount); i < realCount; i++) {
+ V8Array v8Row = v8Table.getArray(i);
+ List