去掉persistence.xml的监听文件变化功能,该有watch组件提供动态修改数据源配置
This commit is contained in:
@@ -362,6 +362,14 @@ public final class Application {
|
|||||||
return new ArrayList<>(servers);
|
return new ArrayList<>(servers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<DataSource> getDataSources() {
|
||||||
|
return new ArrayList<>(dataSources);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<CacheSource> getCacheSources() {
|
||||||
|
return new ArrayList<>(cacheSources);
|
||||||
|
}
|
||||||
|
|
||||||
public File getHome() {
|
public File getHome() {
|
||||||
return home;
|
return home;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
package org.redkale.boot.watch;
|
package org.redkale.boot.watch;
|
||||||
|
|
||||||
import org.redkale.service.AbstractService;
|
import org.redkale.service.AbstractService;
|
||||||
|
import org.redkale.util.Comment;
|
||||||
import org.redkale.watch.WatchService;
|
import org.redkale.watch.WatchService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,4 +15,6 @@ import org.redkale.watch.WatchService;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractWatchService extends AbstractService implements WatchService {
|
public abstract class AbstractWatchService extends AbstractService implements WatchService {
|
||||||
|
|
||||||
|
@Comment("缺少参数")
|
||||||
|
public static final int RET_WATCH_PARAMS_ILLEGAL = 1600_0001;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.redkale.boot.watch;
|
package org.redkale.boot.watch;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Properties;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import org.redkale.boot.Application;
|
import org.redkale.boot.Application;
|
||||||
import org.redkale.net.TransportFactory;
|
import org.redkale.net.http.*;
|
||||||
import org.redkale.net.http.RestService;
|
import org.redkale.service.*;
|
||||||
|
import org.redkale.source.*;
|
||||||
|
import org.redkale.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -17,10 +22,58 @@ import org.redkale.net.http.RestService;
|
|||||||
@RestService(name = "source", catalog = "watch", repair = false)
|
@RestService(name = "source", catalog = "watch", repair = false)
|
||||||
public class SourceWatchService extends AbstractWatchService {
|
public class SourceWatchService extends AbstractWatchService {
|
||||||
|
|
||||||
|
@Comment("不存在的Source")
|
||||||
|
public static final int RET_SOURCE_NOT_EXISTS = 1605_0001;
|
||||||
|
|
||||||
|
@Comment("Source不支持getReadPoolSource/getWritePoolSource方法")
|
||||||
|
public static final int RET_SOURCE_CHANGE_METHOD_NOT_EXISTS = 1605_0002;
|
||||||
|
|
||||||
|
@Comment("PoolSource调用change方法失败")
|
||||||
|
public static final int RET_SOURCE_METHOD_INVOKE_NOT_EXISTS = 1605_0003;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private Application application;
|
private Application application;
|
||||||
|
|
||||||
@Resource
|
@RestMapping(name = "change", auth = false, comment = "动态更改DataSource的配置")
|
||||||
private TransportFactory transportFactory;
|
public RetResult addNode(@RestParam(name = "name", comment = "DataSource的标识") final String name,
|
||||||
|
@RestParam(name = "properties", comment = "配置") final Properties properties) throws IOException {
|
||||||
|
if (name == null) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param (name)");
|
||||||
|
if (properties == null) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param (properties)");
|
||||||
|
DataSource source = null;
|
||||||
|
for (DataSource s : application.getDataSources()) {
|
||||||
|
String resName = ((Resourcable) s).resourceName();
|
||||||
|
if (resName == null) continue;
|
||||||
|
if (!resName.equals(name)) continue;
|
||||||
|
source = s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (source == null) return new RetResult(RET_SOURCE_NOT_EXISTS, "not found source (name = " + name + ")");
|
||||||
|
Method readPoolMethod = null;
|
||||||
|
Method writePoolMethod = null;
|
||||||
|
Class stype = source.getClass();
|
||||||
|
do {
|
||||||
|
for (Method m : stype.getDeclaredMethods()) {
|
||||||
|
if (!PoolSource.class.isAssignableFrom(m.getReturnType())) continue;
|
||||||
|
if (m.getParameterCount() != 0) continue;
|
||||||
|
if (m.getName().equals("getReadPoolSource")) {
|
||||||
|
readPoolMethod = m;
|
||||||
|
} else if (m.getName().equals("getWritePoolSource")) {
|
||||||
|
writePoolMethod = m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while ((stype = stype.getSuperclass()) != Object.class);
|
||||||
|
if (readPoolMethod == null) return new RetResult(RET_SOURCE_CHANGE_METHOD_NOT_EXISTS, "not found source method(getReadPoolSource)");
|
||||||
|
if (writePoolMethod == null) return new RetResult(RET_SOURCE_CHANGE_METHOD_NOT_EXISTS, "not found source method(getWritePoolSource)");
|
||||||
|
readPoolMethod.setAccessible(true);
|
||||||
|
writePoolMethod.setAccessible(true);
|
||||||
|
try {
|
||||||
|
PoolSource readPoolSource = (PoolSource) readPoolMethod.invoke(source);
|
||||||
|
readPoolSource.change(properties);
|
||||||
|
PoolSource writePoolSource = (PoolSource) writePoolMethod.invoke(source);
|
||||||
|
writePoolSource.change(properties);
|
||||||
|
return RetResult.success();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return new RetResult(RET_SOURCE_METHOD_INVOKE_NOT_EXISTS, "poolsource invoke method('change') error");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.redkale.source;
|
package org.redkale.source;
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.file.*;
|
|
||||||
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
|
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
@@ -27,8 +23,6 @@ import static org.redkale.source.DataSources.*;
|
|||||||
*/
|
*/
|
||||||
public class PoolJdbcSource extends PoolSource<Connection> {
|
public class PoolJdbcSource extends PoolSource<Connection> {
|
||||||
|
|
||||||
private static final Map<String, AbstractMap.SimpleEntry<WatchService, List<WeakReference<PoolJdbcSource>>>> maps = new HashMap<>();
|
|
||||||
|
|
||||||
private final ConnectionPoolDataSource source;
|
private final ConnectionPoolDataSource source;
|
||||||
|
|
||||||
private final ArrayBlockingQueue<PooledConnection> queue;
|
private final ArrayBlockingQueue<PooledConnection> queue;
|
||||||
@@ -68,12 +62,6 @@ public class PoolJdbcSource extends PoolSource<Connection> {
|
|||||||
logger.log(Level.WARNING, "connectionErronOccurred [" + event.getSQLException().getSQLState() + "]", event.getSQLException());
|
logger.log(Level.WARNING, "connectionErronOccurred [" + event.getSQLException().getSQLState() + "]", event.getSQLException());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
|
||||||
this.watch();
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.log(Level.WARNING, DataSource.class.getSimpleName() + " watch " + persistxml + " error", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConnectionPoolDataSource createDataSource(Properties property) {
|
private static ConnectionPoolDataSource createDataSource(Properties property) {
|
||||||
@@ -152,74 +140,14 @@ public class PoolJdbcSource extends PoolSource<Connection> {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void watch() throws IOException {
|
|
||||||
if (persistxml == null || unitName == null) return;
|
|
||||||
final String file = persistxml.getFile();
|
|
||||||
final File f = new File(file);
|
|
||||||
if (!f.isFile() || !f.canRead()) return;
|
|
||||||
synchronized (maps) {
|
|
||||||
AbstractMap.SimpleEntry<WatchService, List<WeakReference<PoolJdbcSource>>> entry = maps.get(file);
|
|
||||||
if (entry != null) {
|
|
||||||
entry.getValue().add(new WeakReference<>(this));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final WatchService watcher = f.toPath().getFileSystem().newWatchService();
|
|
||||||
final List<WeakReference<PoolJdbcSource>> list = new CopyOnWriteArrayList<>();
|
|
||||||
Thread watchThread = new Thread() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
while (!this.isInterrupted()) {
|
|
||||||
final WatchKey key = watcher.take();
|
|
||||||
long d; //防止文件正在更新过程中去读取
|
|
||||||
for (;;) {
|
|
||||||
d = f.lastModified();
|
|
||||||
Thread.sleep(2000L);
|
|
||||||
if (d == f.lastModified()) break;
|
|
||||||
}
|
|
||||||
final Map<String, Properties> m = loadPersistenceXml(new FileInputStream(file));
|
|
||||||
key.pollEvents().stream().forEach((event) -> {
|
|
||||||
if (event.kind() != ENTRY_MODIFY) return;
|
|
||||||
if (!((Path) event.context()).toFile().getName().equals(f.getName())) return;
|
|
||||||
for (WeakReference<PoolJdbcSource> ref : list) {
|
|
||||||
PoolJdbcSource pool = ref.get();
|
|
||||||
if (pool == null) continue;
|
|
||||||
try {
|
|
||||||
Properties property = m.get(unitName);
|
|
||||||
if (property == null) property = m.get(unitName + "." + pool.rwtype);
|
|
||||||
if (property != null) pool.change(property);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
logger.log(Level.INFO, event.context() + " occur error", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
key.reset();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.log(Level.WARNING, "DataSource watch " + file + " occur error", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
f.getParentFile().toPath().register(watcher, ENTRY_MODIFY);
|
|
||||||
watchThread.setName("DataSource-Watch-" + maps.size() + "-Thread");
|
|
||||||
watchThread.setDaemon(true);
|
|
||||||
watchThread.start();
|
|
||||||
logger.log(Level.INFO, watchThread.getName() + " start watching " + file);
|
|
||||||
//-----------------------------------------------------------
|
|
||||||
list.add(new WeakReference<>(this));
|
|
||||||
maps.put(file, new AbstractMap.SimpleEntry<>(watcher, list));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void change(Properties property) {
|
public void change(Properties property) {
|
||||||
Method seturlm;
|
Method seturlm;
|
||||||
Class clazz = source.getClass();
|
Class clazz = source.getClass();
|
||||||
String newurl = property.getProperty(JDBC_URL);
|
String newurl = property.getProperty(JDBC_URL, this.url);
|
||||||
String newuser = property.getProperty(JDBC_USER);
|
String newuser = property.getProperty(JDBC_USER, this.username);
|
||||||
String newpassword = property.getProperty(JDBC_PWD);
|
String newpassword = property.getProperty(JDBC_PWD, this.password);
|
||||||
if (this.url.equals(newurl) && this.username.equals(newuser) && this.password.equals(newpassword)) return;
|
if (Objects.equals(this.url, newurl) && Objects.equals(this.username, newuser) && Objects.equals(this.password, newpassword)) return;
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
seturlm = clazz.getMethod("setUrl", String.class);
|
seturlm = clazz.getMethod("setUrl", String.class);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import java.io.IOException;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.*;
|
import java.nio.channels.*;
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
import java.util.Properties;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.logging.*;
|
import java.util.logging.*;
|
||||||
import org.redkale.net.AsyncConnection;
|
import org.redkale.net.AsyncConnection;
|
||||||
@@ -70,10 +70,10 @@ public abstract class PoolTcpSource extends PoolSource<AsyncConnection> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void change(Properties prop) {
|
public void change(Properties prop) {
|
||||||
String newurl = prop.getProperty(JDBC_URL);
|
String newurl = prop.getProperty(JDBC_URL, this.url);
|
||||||
String newuser = prop.getProperty(JDBC_USER, "");
|
String newuser = prop.getProperty(JDBC_USER, this.username);
|
||||||
String newpassword = prop.getProperty(JDBC_PWD, "");
|
String newpassword = prop.getProperty(JDBC_PWD, this.password);
|
||||||
if (this.url.equals(newurl) && this.username.equals(newuser) && this.password.equals(newpassword)) return;
|
if (Objects.equals(this.url, newurl) && Objects.equals(this.username, newuser) && Objects.equals(this.password, newpassword)) return;
|
||||||
this.url = newurl;
|
this.url = newurl;
|
||||||
this.username = newuser;
|
this.username = newuser;
|
||||||
this.password = newpassword;
|
this.password = newpassword;
|
||||||
|
|||||||
Reference in New Issue
Block a user