fixed issue #2099 , support user/passwd acl and canal admin operator

This commit is contained in:
agapple
2019-08-26 18:16:33 +08:00
parent e376a129ed
commit 3d4dbc9a8c
54 changed files with 8518 additions and 813 deletions

View File

@@ -12,6 +12,12 @@
<artifactId>canal-admin-server</artifactId>
<dependencies>
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.protocol</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
@@ -134,6 +140,63 @@
</resources>
</configuration>
</execution>
<execution>
<id>copy canal default conf</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>src/main/resources/conf</outputDirectory>
<overwrite>true</overwrite>
<resources>
<resource>
<directory>${project.parent.basedir}/../deployer/src/main/resources</directory>
<includes>
<include>canal.properties</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>copy instance default conf</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>src/main/resources/conf</outputDirectory>
<overwrite>true</overwrite>
<resources>
<resource>
<directory>${project.parent.basedir}/../deployer/src/main/resources/example</directory>
<includes>
<include>instance.properties</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>copy client default conf</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>src/main/resources/conf</outputDirectory>
<overwrite>true</overwrite>
<resources>
<resource>
<directory>${project.parent.basedir}/../client-adapter/launcher/src/main/resources</directory>
<includes>
<include>application.yml</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>

View File

@@ -25,23 +25,25 @@ import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.SERVICE_UNAVAILABLE)
public class ServiceException extends RuntimeException {
public ServiceException() {
}
private static final long serialVersionUID = -663217666968123330L;
public ServiceException(String message) {
super(message);
}
public ServiceException(){
}
public ServiceException(String message, Throwable cause) {
super(message, cause);
}
public ServiceException(String message){
super(message);
}
public ServiceException(Throwable cause) {
super(cause);
}
public ServiceException(String message, Throwable cause){
super(message, cause);
}
public ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
public ServiceException(Throwable cause){
super(cause);
}
public ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace){
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@@ -0,0 +1,124 @@
package com.alibaba.otter.canal.admin.connector;
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
import com.alibaba.otter.canal.protocol.exception.CanalClientException;
/**
* canal数据操作客户端
*
* @author zebin.xuzb @ 2012-6-19
* @author jianghang
* @version 1.0.0
*/
public interface AdminConnector {
/**
* 链接对应的canal server
*
* @throws CanalClientException
*/
void connect() throws ServiceException;
/**
* 释放链接
*
* @throws CanalClientException
*/
void disconnect() throws ServiceException;
/**
* 获取Canal Server状态
*
* @return 状态代码
*/
boolean check();
/**
* 启动Canal Server
*
* @return 是否成功
*/
boolean start();
/**
* 停止Canal Server
*
* @return 是否成功
*/
boolean stop();
/**
* 重启Canal Server
*
* @return 是否成功
*/
boolean restart();
/**
* 获取所有当前节点下运行中的实例
*
* @return 实例信息
*/
String getRunningInstances();
/**
* 通过实例名检查
*
* @param destination
* @return
*/
boolean checkInstance(String destination);
/**
* 通过实例名启动实例
*
* @param destination 实例名
* @return 是否成功
*/
boolean startInstance(String destination);
/**
* 通过实例名关闭实例
*
* @param destination 实例名
* @return 是否成功
*/
boolean stopInstance(String destination);
/**
* 通过实例名重启实例
*
* @param destination 实例名
* @return 是否成功
*/
boolean restartInstance(String destination);
/**
* 获取Canal Server日志列表
*
* @return 日志信息
*/
String listCanalLog();
/**
* 获取Canal Server日志
*
* @return 日志信息
*/
String canalLog(int lines);
/**
* 获取Instance的机器日志列表
*
* @param destination
*/
String listInstanceLog(String destination);
/**
* 通过实例名获取实例日志
*
* @return 日志信息
*/
String instanceLog(String destination, String fileName, int lines);
}

View File

@@ -0,0 +1,318 @@
package com.alibaba.otter.canal.admin.connector;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.Channel;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.lang.BooleanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
import com.alibaba.otter.canal.protocol.AdminPacket.Ack;
import com.alibaba.otter.canal.protocol.AdminPacket.ClientAuth;
import com.alibaba.otter.canal.protocol.AdminPacket.Handshake;
import com.alibaba.otter.canal.protocol.AdminPacket.InstanceAdmin;
import com.alibaba.otter.canal.protocol.AdminPacket.LogAdmin;
import com.alibaba.otter.canal.protocol.AdminPacket.Packet;
import com.alibaba.otter.canal.protocol.AdminPacket.PacketType;
import com.alibaba.otter.canal.protocol.AdminPacket.ServerAdmin;
import com.alibaba.otter.canal.protocol.SecurityUtil;
import com.alibaba.otter.canal.protocol.exception.CanalClientException;
import com.google.protobuf.ByteString;
/**
* 基于netty实现的admin控制
*
* @author agapple 2019年8月26日 上午10:23:44
* @since 1.1.4
*/
public class SimpleAdminConnector implements AdminConnector {
private static final Logger logger = LoggerFactory.getLogger(SimpleAdminConnector.class);
private String user;
private String passwd;
private SocketAddress address;
private int soTimeout = 60000; // milliseconds
private int idleTimeout = 60 * 60 * 1000; // client和server之间的空闲链接超时的时间,默认为1小时
private final ByteBuffer readHeader = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN);
private final ByteBuffer writeHeader = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN);
private SocketChannel channel;
private ReadableByteChannel readableChannel;
private WritableByteChannel writableChannel;
private volatile boolean connected = false;
public SimpleAdminConnector(String ip, int port, String user, String passwd){
this.address = new InetSocketAddress(ip, port);
this.user = user;
this.passwd = passwd;
}
@Override
public void connect() throws ServiceException {
try {
if (connected) {
return;
}
channel = SocketChannel.open();
channel.socket().setSoTimeout(soTimeout);
channel.connect(address);
readableChannel = Channels.newChannel(channel.socket().getInputStream());
writableChannel = Channels.newChannel(channel.socket().getOutputStream());
Packet p = Packet.parseFrom(readNextPacket());
if (p.getVersion() != 1) {
throw new CanalClientException("unsupported version at this client.");
}
if (p.getType() != PacketType.HANDSHAKE) {
throw new CanalClientException("expect handshake but found other type.");
}
Handshake handshake = Handshake.parseFrom(p.getBody());
ByteString seed = handshake.getSeeds(); // seed for auth
String newPasswd = passwd;
if (passwd != null) {
// encode passwd
newPasswd = SecurityUtil.byte2HexStr(SecurityUtil.scramble411(passwd.getBytes(), seed.toByteArray()));
}
ClientAuth ca = ClientAuth.newBuilder()
.setUsername(user != null ? user : "")
.setPassword(ByteString.copyFromUtf8(newPasswd != null ? newPasswd : ""))
.setNetReadTimeout(idleTimeout)
.setNetWriteTimeout(idleTimeout)
.build();
writeWithHeader(Packet.newBuilder()
.setType(PacketType.CLIENTAUTHENTICATION)
.setBody(ca.toByteString())
.build()
.toByteArray());
//
Packet ack = Packet.parseFrom(readNextPacket());
if (ack.getType() != PacketType.ACK) {
throw new CanalClientException("unexpected packet type when ack is expected");
}
Ack ackBody = Ack.parseFrom(ack.getBody());
if (ackBody.getCode() > 0) {
throw new ServiceException("something goes wrong when doing authentication: " + ackBody.getMessage());
}
connected = true;
} catch (IOException e) {
throw new ServiceException(e);
} catch (NoSuchAlgorithmException e) {
throw new ServiceException(e);
}
}
@Override
public void disconnect() throws ServiceException {
if (!connected) {
return;
}
connected = false;
if (readableChannel != null) {
quietlyClose(readableChannel);
readableChannel = null;
}
if (writableChannel != null) {
quietlyClose(writableChannel);
writableChannel = null;
}
if (channel != null) {
quietlyClose(channel);
channel = null;
}
}
@Override
public boolean check() {
return BooleanUtils.toBoolean(Integer.valueOf(doServerAdmin("check")));
}
@Override
public boolean start() {
return BooleanUtils.toBoolean(Integer.valueOf(doServerAdmin("start")));
}
@Override
public boolean stop() {
return BooleanUtils.toBoolean(Integer.valueOf(doServerAdmin("stop")));
}
@Override
public boolean restart() {
return BooleanUtils.toBoolean(Integer.valueOf(doServerAdmin("restart")));
}
@Override
public String getRunningInstances() {
return doServerAdmin("list");
}
@Override
public boolean checkInstance(String destination) {
return BooleanUtils.toBoolean(Integer.valueOf(doInstanceAdmin(destination, "check")));
}
@Override
public boolean startInstance(String destination) {
return BooleanUtils.toBoolean(Integer.valueOf(doInstanceAdmin(destination, "start")));
}
@Override
public boolean stopInstance(String destination) {
return BooleanUtils.toBoolean(Integer.valueOf(doInstanceAdmin(destination, "stop")));
}
@Override
public boolean restartInstance(String destination) {
return BooleanUtils.toBoolean(Integer.valueOf(doInstanceAdmin(destination, "restart")));
}
@Override
public String listCanalLog() {
return doLogAdmin("server", "list", null, null, 0);
}
@Override
public String canalLog(int lines) {
return doLogAdmin("server", "file", null, null, lines);
}
@Override
public String listInstanceLog(String destination) {
return doLogAdmin("instance", "list", destination, null, 0);
}
@Override
public String instanceLog(String destination, String fileName, int lines) {
return doLogAdmin("instance", "file", destination, fileName, lines);
}
// ==================== helper method ====================
private String doServerAdmin(String action) {
try {
writeWithHeader(Packet.newBuilder()
.setType(PacketType.SERVER)
.setBody(ServerAdmin.newBuilder().setAction(action).build().toByteString())
.build()
.toByteArray());
Packet p = Packet.parseFrom(readNextPacket());
Ack ack = Ack.parseFrom(p.getBody());
if (ack.getCode() > 0) {
throw new ServiceException("failed to subscribe with reason: " + ack.getMessage());
}
return ack.getMessage();
} catch (IOException e) {
throw new ServiceException(e);
}
}
private String doInstanceAdmin(String destination, String action) {
try {
writeWithHeader(Packet.newBuilder()
.setType(PacketType.INSTANCE)
.setBody(InstanceAdmin.newBuilder()
.setDestination(destination)
.setAction(action)
.build()
.toByteString())
.build()
.toByteArray());
Packet p = Packet.parseFrom(readNextPacket());
Ack ack = Ack.parseFrom(p.getBody());
if (ack.getCode() > 0) {
throw new ServiceException("failed to subscribe with reason: " + ack.getMessage());
}
return ack.getMessage();
} catch (IOException e) {
throw new ServiceException(e);
}
}
private String doLogAdmin(String type, String action, String destination, String file, int count) {
try {
writeWithHeader(Packet.newBuilder()
.setType(PacketType.LOG)
.setBody(LogAdmin.newBuilder()
.setType(type)
.setAction(action)
.setDestination(destination == null ? "" : destination)
.setFile(file == null ? "" : file)
.setCount(count)
.build()
.toByteString())
.build()
.toByteArray());
Packet p = Packet.parseFrom(readNextPacket());
Ack ack = Ack.parseFrom(p.getBody());
if (ack.getCode() > 0) {
throw new ServiceException("failed to subscribe with reason: " + ack.getMessage());
}
return ack.getMessage();
} catch (IOException e) {
throw new ServiceException(e);
}
}
private void writeWithHeader(byte[] body) throws IOException {
writeWithHeader(writableChannel, body);
}
private byte[] readNextPacket() throws IOException {
return readNextPacket(readableChannel);
}
private void writeWithHeader(WritableByteChannel channel, byte[] body) throws IOException {
writeHeader.clear();
writeHeader.putInt(body.length);
writeHeader.flip();
channel.write(writeHeader);
channel.write(ByteBuffer.wrap(body));
}
private byte[] readNextPacket(ReadableByteChannel channel) throws IOException {
readHeader.clear();
read(channel, readHeader);
int bodyLen = readHeader.getInt(0);
ByteBuffer bodyBuf = ByteBuffer.allocate(bodyLen).order(ByteOrder.BIG_ENDIAN);
read(channel, bodyBuf);
return bodyBuf.array();
}
private void read(ReadableByteChannel channel, ByteBuffer buffer) throws IOException {
while (buffer.hasRemaining()) {
int r = channel.read(buffer);
if (r == -1) {
throw new IOException("end of stream when reading header");
}
}
}
private void quietlyClose(Channel channel) {
try {
channel.close();
} catch (IOException e) {
logger.warn("exception on closing channel:{} \n {}", channel, e);
}
}
}

View File

@@ -0,0 +1,31 @@
package com.alibaba.otter.canal.admin.connector;
import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SimpleAdminConnectors {
private static final Logger logger = LoggerFactory.getLogger(SimpleAdminConnectors.class);
private static String defaultUser = "admin";
private static String defaultPasswd = "admin";
public static <R> R execute(String ip, int port, Function<AdminConnector, R> function) {
return execute(ip, port, defaultUser, defaultPasswd, function);
}
public static <R> R execute(String ip, int port, String user, String passwd, Function<AdminConnector, R> function) {
SimpleAdminConnector connector = new SimpleAdminConnector(ip, port, user, passwd);
try {
connector.connect();
return function.apply(connector);
} catch (Exception e) {
logger.error(e.getMessage());
} finally {
connector.disconnect();
}
return null;
}
}

View File

@@ -4,7 +4,14 @@ import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.otter.canal.admin.model.BaseModel;
import com.alibaba.otter.canal.admin.model.CanalInstanceConfig;
@@ -49,6 +56,18 @@ public class CanalInstanceController {
return BaseModel.getInstance("success");
}
/**
* 实例详情信息
*
* @param id 实例配置id
* @param env 环境变量
* @return 实例信息
*/
@GetMapping(value = "/instance")
public BaseModel<CanalInstanceConfig> config(@PathVariable Long id, @PathVariable String env) {
return BaseModel.getInstance(canalInstanceConfigService.detail(id));
}
/**
* 实例详情信息
*

View File

@@ -1,90 +0,0 @@
package com.alibaba.otter.canal.admin.jmx;
/**
* Canal Server JMX MBean
*
* @author rewerma 2019-07-13 下午05:12:16
* @version 1.0.0
*/
public interface CanalServerMXBean {
/**
* 获取Canal Server状态
*
* @return 状态代码
*/
int getStatus();
/**
* 启动Canal Server
*
* @return 是否成功
*/
boolean start();
/**
* 停止Canal Server
*
* @return 是否成功
*/
boolean stop();
/**
* 重启Canal Server
*
* @return 是否成功
*/
boolean restart();
/**
* 退出Canal Server(关闭进程)
*
* @return 是否成功
*/
boolean exit();
/**
* 通过实例名启动实例
*
* @param destination 实例名
* @return 是否成功
*/
boolean startInstance(String destination);
/**
* 通过实例名关闭实例
*
* @param destination 实例名
* @return 是否成功
*/
boolean stopInstance(String destination);
/**
* 通过实例名重启实例
*
* @param destination 实例名
* @return 是否成功
*/
boolean reloadInstance(String destination);
/**
* 获取所有当前节点下运行中的实例
*
* @return 实例信息
*/
String getRunningInstances();
/**
* 获取Canal Server日志(末尾100行)
*
* @return 日志信息
*/
String canalLog();
/**
* 通过实例名获取实例日志(末尾100行)
*
* @return 日志信息
*/
String instanceLog(String destination);
}

View File

@@ -1,164 +0,0 @@
package com.alibaba.otter.canal.admin.jmx;
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.management.*;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.SocketTimeoutException;
import java.util.concurrent.*;
import java.util.function.Function;
/**
* Canal Server JMX 控制层
*
* @author rewerma 2019-07-13 下午05:12:16
* @version 1.0.0
*/
public class JMXConnection {
private static final Logger logger = LoggerFactory.getLogger(JMXConnection.class);
private String ip;
private Integer port;
private JMXConnector jmxc;
private CanalServerMXBean canalServerMXBean;
public JMXConnection(String ip, Integer port){
this.ip = ip;
this.port = port;
}
/**
* 执行相关操作
*
* @param ip jmx ip
* @param port jmx port
* @param function 执行方法
* @param <R> 返回泛型
* @return 执行结果
*/
public static <R> R execute(String ip, int port, Function<CanalServerMXBean, R> function) {
JMXConnection jmxConnection = new JMXConnection(ip, port);
try {
CanalServerMXBean canalServerMXBean = jmxConnection.getCanalServerMXBean();
return function.apply(canalServerMXBean);
} catch (Exception e) {
logger.error(e.getMessage());
} finally {
jmxConnection.close();
}
return null;
}
/**
* 连接远程jmx
*/
public void connect() {
try {
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + ip + ":" + port + "/jmxrmi");
// jmxc = JMXConnectorFactory.connect(url, null);
jmxc = connectWithTimeout(url, 3, TimeUnit.SECONDS);
MBeanServerConnection mBeanServerConnection = jmxc.getMBeanServerConnection();
ObjectName name = new ObjectName("CanalServerAgent:type=CanalServerStatus");
mBeanServerConnection.addNotificationListener(name, (notification, handback) -> {
}, null, null);
canalServerMXBean = MBeanServerInvocationHandler
.newProxyInstance(mBeanServerConnection, name, CanalServerMXBean.class, false);
} catch (Exception e) {
throw new ServiceException(e.getMessage(), e);
}
}
/**
* 带超时的jmx连接器
*
* @param url jmx url
* @param timeout 超时时间
* @param unit 超时时间单位
* @return JMXConnector
* @throws IOException 连接异常
*/
private static JMXConnector connectWithTimeout(final JMXServiceURL url, long timeout,
TimeUnit unit) throws IOException {
final BlockingQueue<Object> mailbox = new ArrayBlockingQueue<>(1);
ExecutorService executor = Executors.newSingleThreadExecutor(daemonThreadFactory);
executor.submit(() -> {
try {
JMXConnector connector = JMXConnectorFactory.connect(url);
if (!mailbox.offer(connector)) connector.close();
} catch (Throwable t) {
mailbox.offer(t);
}
});
Object result;
try {
result = mailbox.poll(timeout, unit);
if (result == null) {
if (!mailbox.offer("")) result = mailbox.take();
}
} catch (InterruptedException e) {
throw initCause(new InterruptedIOException(e.getMessage()), e);
} finally {
executor.shutdown();
}
if (result == null) throw new SocketTimeoutException("Connect timed out: " + url);
if (result instanceof JMXConnector) return (JMXConnector) result;
try {
throw (Throwable) result;
} catch (IOException | RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
// In principle this can't happen but we wrap it anyway
throw new IOException(e.toString(), e);
}
}
private static <T extends Throwable> T initCause(T wrapper, Throwable wrapped) {
wrapper.initCause(wrapped);
return wrapper;
}
private static class DaemonThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
Thread t = Executors.defaultThreadFactory().newThread(r);
t.setDaemon(true);
return t;
}
}
private static final ThreadFactory daemonThreadFactory = new DaemonThreadFactory();
/**
* 获取MBean
*
* @return canalServerMXBean
*/
public CanalServerMXBean getCanalServerMXBean() {
if (jmxc == null) {
connect();
}
return canalServerMXBean;
}
/**
* 关闭jmx连接
*/
public void close() {
try {
if (jmxc != null) {
jmxc.close();
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
jmxc = null;
}
}
}

View File

@@ -2,12 +2,14 @@ package com.alibaba.otter.canal.admin.model;
import io.ebean.Ebean;
import io.ebean.EbeanServer;
import org.apache.commons.beanutils.PropertyUtils;
import java.lang.reflect.Field;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.OptimisticLockException;
import java.lang.reflect.Field;
import org.apache.commons.beanutils.PropertyUtils;
/**
* EBean Model扩展类

View File

@@ -1,5 +1,13 @@
package com.alibaba.otter.canal.admin.service.impl;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.alibaba.otter.canal.admin.model.CanalConfig;
@@ -14,15 +22,75 @@ import com.alibaba.otter.canal.admin.service.CanalConfigService;
@Service
public class CanalConfigServiceImpl implements CanalConfigService {
private Logger logger = LoggerFactory.getLogger(CanalConfigServiceImpl.class);
private static final String CANAL_GLOBAL_CONFIG = "canal.properties";
private static final String CANAL_ADAPTER_CONFIG = "application.yml";
public CanalConfig getCanalConfig() {
return CanalConfig.find.byId(1L);
long id = 1L;
CanalConfig config = CanalConfig.find.byId(id);
if (config == null) {
String context = loadDefaultConf(CANAL_GLOBAL_CONFIG);
if (context == null) {
return null;
}
config = new CanalConfig();
config.setId(id);
config.setName(CANAL_GLOBAL_CONFIG);
config.setModifiedTime(new Date());
config.setContent(context);
return config;
}
return config;
}
public CanalConfig getAdapterConfig() {
return CanalConfig.find.byId(2L);
long id = 2L;
CanalConfig config = CanalConfig.find.byId(id);
if (config == null) {
String context = loadDefaultConf(CANAL_ADAPTER_CONFIG);
if (context == null) {
return null;
}
config = new CanalConfig();
config.setId(id);
config.setName(CANAL_ADAPTER_CONFIG);
config.setModifiedTime(new Date());
config.setContent(context);
return config;
}
return config;
}
public void updateContent(CanalConfig canalConfig) {
canalConfig.update("content");
try {
canalConfig.insert();
} catch (Throwable e) {
canalConfig.update();
}
}
private String loadDefaultConf(String confFileName) {
InputStream input = null;
try {
input = Thread.currentThread().getContextClassLoader().getResourceAsStream("conf/" + confFileName);
if (input == null) {
return null;
}
return StringUtils.join(IOUtils.readLines(input), "\n");
} catch (IOException e) {
logger.error("find " + confFileName + " is error!", e);
return null;
} finally {
if (input != null) {
IOUtils.closeQuietly(input);
}
}
}
}

View File

@@ -1,5 +1,7 @@
package com.alibaba.otter.canal.admin.service.impl;
import io.ebean.Query;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -7,14 +9,12 @@ import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import com.alibaba.otter.canal.admin.jmx.CanalServerMXBean;
import com.alibaba.otter.canal.admin.jmx.JMXConnection;
import com.alibaba.otter.canal.admin.connector.AdminConnector;
import com.alibaba.otter.canal.admin.connector.SimpleAdminConnectors;
import com.alibaba.otter.canal.admin.model.CanalInstanceConfig;
import com.alibaba.otter.canal.admin.model.NodeServer;
import com.alibaba.otter.canal.admin.service.CanalInstanceService;
import io.ebean.Query;
/**
* Canal实例配置信息业务层
*
@@ -39,8 +39,9 @@ public class CanalInstanceServiceImpl implements CanalInstanceService {
// check all canal instances running status
List<NodeServer> nodeServers = NodeServer.find.query().findList();
for (NodeServer nodeServer : nodeServers) {
String runningInstances = JMXConnection
.execute(nodeServer.getIp(), nodeServer.getPort(), CanalServerMXBean::getRunningInstances);
String runningInstances = SimpleAdminConnectors.execute(nodeServer.getIp(),
nodeServer.getPort(),
AdminConnector::getRunningInstances);
if (runningInstances == null) {
continue;
}
@@ -90,9 +91,9 @@ public class CanalInstanceServiceImpl implements CanalInstanceService {
return result;
}
String log = JMXConnection.execute(nodeServer.getIp(),
String log = SimpleAdminConnectors.execute(nodeServer.getIp(),
nodeServer.getPort(),
canalServerMXBean -> canalServerMXBean.instanceLog(canalInstanceConfig.getName()));
adminConnector -> adminConnector.instanceLog(canalInstanceConfig.getName(), null, 100));
result.put("instance", canalInstanceConfig.getName());
result.put("log", log);
@@ -122,13 +123,13 @@ public class CanalInstanceServiceImpl implements CanalInstanceService {
}
Boolean resutl = null;
if ("start".equals(option)) {
resutl = JMXConnection.execute(nodeServer.getIp(),
resutl = SimpleAdminConnectors.execute(nodeServer.getIp(),
nodeServer.getPort(),
canalServerMXBean -> canalServerMXBean.startInstance(canalInstanceConfig.getName()));
adminConnector -> adminConnector.startInstance(canalInstanceConfig.getName()));
} else if ("stop".equals(option)) {
resutl = JMXConnection.execute(nodeServer.getIp(),
resutl = SimpleAdminConnectors.execute(nodeServer.getIp(),
nodeServer.getPort(),
canalServerMXBean -> canalServerMXBean.stopInstance(canalInstanceConfig.getName()));
adminConnector -> adminConnector.stopInstance(canalInstanceConfig.getName()));
} else {
return false;
}

View File

@@ -1,5 +1,7 @@
package com.alibaba.otter.canal.admin.service.impl;
import io.ebean.Query;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
@@ -8,18 +10,14 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
import com.alibaba.otter.canal.admin.jmx.CanalServerMXBean;
import com.alibaba.otter.canal.admin.jmx.JMXConnection;
import com.alibaba.otter.canal.admin.connector.AdminConnector;
import com.alibaba.otter.canal.admin.connector.SimpleAdminConnectors;
import com.alibaba.otter.canal.admin.model.NodeServer;
import com.alibaba.otter.canal.admin.service.NodeServerService;
import io.ebean.Query;
/**
* 节点信息业务层
*
@@ -29,8 +27,6 @@ import io.ebean.Query;
@Service
public class NodeServerServiceImpl implements NodeServerService {
private static final Logger logger = LoggerFactory.getLogger(NodeServerServiceImpl.class);
public void save(NodeServer nodeServer) {
int cnt = NodeServer.find.query()
.where()
@@ -90,18 +86,9 @@ public class NodeServerServiceImpl implements NodeServerService {
// get all nodes status
for (NodeServer ns : nodeServers) {
futures.add(executorService.submit(() -> {
int status = -1;
JMXConnection jmxConnection = new JMXConnection(ns.getIp(), ns.getPort());
try {
CanalServerMXBean canalServerMXBean = jmxConnection.getCanalServerMXBean();
status = canalServerMXBean.getStatus();
} catch (Exception e) {
logger.warn(e.getMessage());
} finally {
jmxConnection.close();
}
ns.setStatus(status);
return status != -1;
boolean status = SimpleAdminConnectors.execute(ns.getIp(), ns.getPort(), AdminConnector::check);
ns.setStatus(status ? 1 : 0);
return !status;
}));
}
futures.forEach(f -> {
@@ -118,11 +105,8 @@ public class NodeServerServiceImpl implements NodeServerService {
}
public int remoteNodeStatus(String ip, Integer port) {
Integer resutl = JMXConnection.execute(ip, port, CanalServerMXBean::getStatus);
if (resutl == null) {
resutl = -1;
}
return resutl;
boolean result = SimpleAdminConnectors.execute(ip, port, AdminConnector::check);
return result ? 1 : 0;
}
public String remoteCanalLog(Long id) {
@@ -130,7 +114,9 @@ public class NodeServerServiceImpl implements NodeServerService {
if (nodeServer == null) {
return "";
}
return JMXConnection.execute(nodeServer.getIp(), nodeServer.getPort(), CanalServerMXBean::canalLog);
return SimpleAdminConnectors.execute(nodeServer.getIp(),
nodeServer.getPort(),
adminConnector -> adminConnector.canalLog(100));
}
public boolean remoteOperation(Long id, String option) {
@@ -138,18 +124,18 @@ public class NodeServerServiceImpl implements NodeServerService {
if (nodeServer == null) {
return false;
}
Boolean resutl = null;
Boolean result = null;
if ("start".equals(option)) {
resutl = JMXConnection.execute(nodeServer.getIp(), nodeServer.getPort(), CanalServerMXBean::start);
result = SimpleAdminConnectors.execute(nodeServer.getIp(), nodeServer.getPort(), AdminConnector::start);
} else if ("stop".equals(option)) {
resutl = JMXConnection.execute(nodeServer.getIp(), nodeServer.getPort(), CanalServerMXBean::stop);
result = SimpleAdminConnectors.execute(nodeServer.getIp(), nodeServer.getPort(), AdminConnector::stop);
} else {
return false;
}
if (resutl == null) {
resutl = false;
if (result == null) {
result = false;
}
return resutl;
return result;
}
}

View File

@@ -1,10 +1,14 @@
package com.alibaba.otter.canal.admin.service.impl;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import com.alibaba.otter.canal.admin.common.exception.ServiceException;
import com.alibaba.otter.canal.admin.model.User;
import com.alibaba.otter.canal.admin.service.UserService;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import com.alibaba.otter.canal.protocol.SecurityUtil;
/**
* 用户信息业务层
@@ -15,11 +19,22 @@ import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
private static byte[] seeds = "canal is best!".getBytes();
public User find4Login(String username, String password) {
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
return null;
}
User user = User.find.query().where().eq("username", username).eq("password", password).findOne();
User user = User.find.query().where().eq("username", username).findOne();
try {
byte[] pass = SecurityUtil.scramble411(password.getBytes(), seeds);
if (!SecurityUtil.scrambleServerAuth(pass, SecurityUtil.hexStr2Bytes(user.getPassword()), seeds)) {
throw new ServiceException("user:" + user.getName() + " passwd incorrect!");
}
} catch (NoSuchAlgorithmException e) {
throw new ServiceException("user:" + user.getName() + " auth failed!");
}
if (user != null) {
user.setPassword("");
}
@@ -27,14 +42,23 @@ public class UserServiceImpl implements UserService {
}
public void update(User user) {
User userTmp = User.find.byId(1L);
User userTmp = User.find.query().where().eq("username", user.getUsername()).findOne();
if (userTmp == null) {
throw new ServiceException();
}
if (!userTmp.getPassword().equals(user.getOldPassword())) {
throw new ServiceException("错误的旧密码");
try {
byte[] pass = SecurityUtil.scramble411(user.getOldPassword().getBytes(), seeds);
if (!SecurityUtil.scrambleServerAuth(pass, SecurityUtil.hexStr2Bytes(userTmp.getPassword()), seeds)) {
throw new ServiceException("old passwd is unmatch");
}
user.setId(userTmp.getId());
user.setPassword(SecurityUtil.scrambleGenPass(user.getPassword().getBytes()));
} catch (NoSuchAlgorithmException e) {
throw new ServiceException("passwd process failed");
}
user.setId(1L);
user.update("username", "nn:password");
}
}

View File

@@ -0,0 +1,85 @@
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `canal_manager` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_bin */;
USE `canal_manager`;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for canal_adapter_config
-- ----------------------------
DROP TABLE IF EXISTS `canal_adapter_config`;
CREATE TABLE `canal_adapter_config` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`category` varchar(45) NOT NULL,
`name` varchar(45) NOT NULL,
`content` text NOT NULL,
`modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Table structure for canal_config
-- ----------------------------
DROP TABLE IF EXISTS `canal_config`;
CREATE TABLE `canal_config` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(45) NOT NULL,
`content` text NOT NULL,
`modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `name_UNIQUE` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Table structure for canal_instance_config
-- ----------------------------
DROP TABLE IF EXISTS `canal_instance_config`;
CREATE TABLE `canal_instance_config` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(45) NOT NULL,
`content` text NOT NULL,
`modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `name_UNIQUE` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Table structure for canal_node_server
-- ----------------------------
DROP TABLE IF EXISTS `canal_node_server`;
CREATE TABLE `canal_node_server` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(63) NOT NULL,
`ip` varchar(63) NOT NULL,
`port` int(11) DEFAULT NULL,
`port2` int(11) DEFAULT NULL,
`status` int(11) NOT NULL,
`modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Table structure for canal_user
-- ----------------------------
DROP TABLE IF EXISTS `canal_user`;
CREATE TABLE `canal_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(31) NOT NULL,
`password` varchar(128) NOT NULL,
`name` varchar(31) NOT NULL,
`roles` varchar(31) NOT NULL,
`introduction` varchar(255) DEFAULT NULL,
`avatar` varchar(255) DEFAULT NULL,
`creation_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of canal_user
-- ----------------------------
BEGIN;
INSERT INTO `canal_user` VALUES (1, 'admin', '6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9', 'Canal Manager', 'admin', NULL, NULL, '2019-07-14 00:05:28');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -1 +1 @@
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=/favicon.ico><title>Canal Admin</title><link href=/static/css/chunk-elementUI.18b11d0e.css rel=stylesheet><link href=/static/css/chunk-libs.5cf311f0.css rel=stylesheet><link href=/static/css/app.bb951cb3.css rel=stylesheet></head><body><noscript><strong>We're sorry but Canal Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/static/js/chunk-elementUI.667f4c87.js></script><script src=/static/js/chunk-libs.c04beefc.js></script><script>(function(e){function n(n){for(var r,c,f=n[0],a=n[1],i=n[2],h=0,l=[];h<f.length;h++)c=f[h],u[c]&&l.push(u[c][0]),u[c]=0;for(r in a)Object.prototype.hasOwnProperty.call(a,r)&&(e[r]=a[r]);d&&d(n);while(l.length)l.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var f=t[c];0!==u[f]&&(r=!1)}r&&(o.splice(n--,1),e=a(a.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function f(e){return a.p+"static/js/"+({}[e]||e)+"."+{"chunk-101fc062":"372a5ca5","chunk-137eaf06":"539d27c7","chunk-238a81e9":"da844a27","chunk-37c49cbf":"92ebe0ae","chunk-3fcdf643":"4b7133b8","chunk-1ef583f0":"c799c803","chunk-5560c396":"5eb91155","chunk-6f6d6145":"385d3c06","chunk-4f09fed2":"d107437b","chunk-69386cf0":"bdbe3f0c","chunk-7ec889b7":"192dc04c","chunk-e1a839e4":"3175ead5"}[e]+".js"}function a(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var n=[],t={"chunk-101fc062":1,"chunk-238a81e9":1,"chunk-37c49cbf":1,"chunk-1ef583f0":1,"chunk-5560c396":1,"chunk-6f6d6145":1,"chunk-4f09fed2":1,"chunk-69386cf0":1,"chunk-7ec889b7":1};c[e]?n.push(c[e]):0!==c[e]&&t[e]&&n.push(c[e]=new Promise(function(n,t){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-101fc062":"fad9926f","chunk-137eaf06":"31d6cfe0","chunk-238a81e9":"e8e2beee","chunk-37c49cbf":"efc21a9c","chunk-3fcdf643":"31d6cfe0","chunk-1ef583f0":"ec3a43f5","chunk-5560c396":"b660603c","chunk-6f6d6145":"bf63041a","chunk-4f09fed2":"70ec0b86","chunk-69386cf0":"741ff14e","chunk-7ec889b7":"c0585512","chunk-e1a839e4":"31d6cfe0"}[e]+".css",u=a.p+r,o=document.getElementsByTagName("link"),f=0;f<o.length;f++){var i=o[f],h=i.getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(h===r||h===u))return n()}var l=document.getElementsByTagName("style");for(f=0;f<l.length;f++){i=l[f],h=i.getAttribute("data-href");if(h===r||h===u)return n()}var d=document.createElement("link");d.rel="stylesheet",d.type="text/css",d.onload=n,d.onerror=function(n){var r=n&&n.target&&n.target.src||u,o=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");o.code="CSS_CHUNK_LOAD_FAILED",o.request=r,delete c[e],d.parentNode.removeChild(d),t(o)},d.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(d)}).then(function(){c[e]=0}));var r=u[e];if(0!==r)if(r)n.push(r[2]);else{var o=new Promise(function(n,t){r=u[e]=[n,t]});n.push(r[2]=o);var i,h=document.createElement("script");h.charset="utf-8",h.timeout=120,a.nc&&h.setAttribute("nonce",a.nc),h.src=f(e),i=function(n){h.onerror=h.onload=null,clearTimeout(l);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var l=setTimeout(function(){i({type:"timeout",target:h})},12e4);h.onerror=h.onload=i,document.head.appendChild(h)}return Promise.all(n)},a.m=e,a.c=r,a.d=function(e,n,t){a.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},a.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,n){if(1&n&&(e=a(e)),8&n)return e;if(4&n&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)a.d(t,r,function(n){return e[n]}.bind(null,r));return t},a.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return a.d(n,"a",n),n},a.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},a.p="/",a.oe=function(e){throw console.error(e),e};var i=window["webpackJsonp"]=window["webpackJsonp"]||[],h=i.push.bind(i);i.push=n,i=i.slice();for(var l=0;l<i.length;l++)n(i[l]);var d=h;t()})([]);</script><script src=/static/js/app.38307ef0.js></script></body></html>
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=/favicon.ico><title>Canal Admin</title><link href=/static/css/chunk-elementUI.18b11d0e.css rel=stylesheet><link href=/static/css/chunk-libs.5cf311f0.css rel=stylesheet><link href=/static/css/app.bb951cb3.css rel=stylesheet></head><body><noscript><strong>We're sorry but Canal Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=/static/js/chunk-elementUI.667f4c87.js></script><script src=/static/js/chunk-libs.c04beefc.js></script><script>(function(e){function n(n){for(var r,c,f=n[0],a=n[1],i=n[2],h=0,d=[];h<f.length;h++)c=f[h],u[c]&&d.push(u[c][0]),u[c]=0;for(r in a)Object.prototype.hasOwnProperty.call(a,r)&&(e[r]=a[r]);l&&l(n);while(d.length)d.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var f=t[c];0!==u[f]&&(r=!1)}r&&(o.splice(n--,1),e=a(a.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function f(e){return a.p+"static/js/"+({}[e]||e)+"."+{"chunk-101fc062":"372a5ca5","chunk-137eaf06":"539d27c7","chunk-238a81e9":"da844a27","chunk-37c49cbf":"92ebe0ae","chunk-3fcdf643":"4b7133b8","chunk-1ef583f0":"c799c803","chunk-5560c396":"5eb91155","chunk-6f6d6145":"385d3c06","chunk-4f09fed2":"d107437b","chunk-69386cf0":"bdbe3f0c","chunk-7ec889b7":"192dc04c","chunk-e1a839e4":"7ee86dd8"}[e]+".js"}function a(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var n=[],t={"chunk-101fc062":1,"chunk-238a81e9":1,"chunk-37c49cbf":1,"chunk-1ef583f0":1,"chunk-5560c396":1,"chunk-6f6d6145":1,"chunk-4f09fed2":1,"chunk-69386cf0":1,"chunk-7ec889b7":1};c[e]?n.push(c[e]):0!==c[e]&&t[e]&&n.push(c[e]=new Promise(function(n,t){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-101fc062":"fad9926f","chunk-137eaf06":"31d6cfe0","chunk-238a81e9":"e8e2beee","chunk-37c49cbf":"efc21a9c","chunk-3fcdf643":"31d6cfe0","chunk-1ef583f0":"ec3a43f5","chunk-5560c396":"b660603c","chunk-6f6d6145":"bf63041a","chunk-4f09fed2":"70ec0b86","chunk-69386cf0":"741ff14e","chunk-7ec889b7":"c0585512","chunk-e1a839e4":"31d6cfe0"}[e]+".css",u=a.p+r,o=document.getElementsByTagName("link"),f=0;f<o.length;f++){var i=o[f],h=i.getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(h===r||h===u))return n()}var d=document.getElementsByTagName("style");for(f=0;f<d.length;f++){i=d[f],h=i.getAttribute("data-href");if(h===r||h===u)return n()}var l=document.createElement("link");l.rel="stylesheet",l.type="text/css",l.onload=n,l.onerror=function(n){var r=n&&n.target&&n.target.src||u,o=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");o.code="CSS_CHUNK_LOAD_FAILED",o.request=r,delete c[e],l.parentNode.removeChild(l),t(o)},l.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(l)}).then(function(){c[e]=0}));var r=u[e];if(0!==r)if(r)n.push(r[2]);else{var o=new Promise(function(n,t){r=u[e]=[n,t]});n.push(r[2]=o);var i,h=document.createElement("script");h.charset="utf-8",h.timeout=120,a.nc&&h.setAttribute("nonce",a.nc),h.src=f(e),i=function(n){h.onerror=h.onload=null,clearTimeout(d);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var d=setTimeout(function(){i({type:"timeout",target:h})},12e4);h.onerror=h.onload=i,document.head.appendChild(h)}return Promise.all(n)},a.m=e,a.c=r,a.d=function(e,n,t){a.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},a.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,n){if(1&n&&(e=a(e)),8&n)return e;if(4&n&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)a.d(t,r,function(n){return e[n]}.bind(null,r));return t},a.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return a.d(n,"a",n),n},a.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},a.p="/",a.oe=function(e){throw console.error(e),e};var i=window["webpackJsonp"]=window["webpackJsonp"]||[],h=i.push.bind(i);i.push=n,i=i.slice();for(var d=0;d<i.length;d++)n(i[d]);var l=h;t()})([]);</script><script src=/static/js/app.38307ef0.js></script></body></html>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,40 @@
package com.alibaba.otter.canal.admin;
import org.junit.Test;
import com.alibaba.otter.canal.admin.connector.SimpleAdminConnector;
public class SimpleAdminConnectorTest {
@Test
public void testSimple() {
SimpleAdminConnector connector = new SimpleAdminConnector("127.0.0.1", 11110, "admin", "admin");
connector.connect();
System.out.println("check 1 : " + connector.check());
System.out.println("getRunning Before stop 1 : " + connector.getRunningInstances());
System.out.println("stop 1: " + connector.stop());
System.out.println("getRunning After stop 1 : " + connector.getRunningInstances());
System.out.println("check 1 : " + connector.check());
System.out.println("listFile 1 : " + connector.listCanalLog());
System.out.println("getFile 1 : " + connector.canalLog(10));
connector.disconnect();
connector.connect();
System.out.println("check 2 : " + connector.check());
System.out.println("get Running Before start : " + connector.getRunningInstances());
System.out.println("start 2 : " + connector.start());
System.out.println("get Running After start : " + connector.getRunningInstances());
System.out.println("check 2 : " + connector.check());
System.out.println("check after before example 2 : " + connector.checkInstance("example"));
System.out.println("stop example 2 : " + connector.stopInstance("example"));
System.out.println("check example 2 : " + connector.checkInstance("example"));
System.out.println("start example 2 : " + connector.startInstance("example"));
System.out.println("check after start example 2 : " + connector.checkInstance("example"));
System.out.println("listFile 2 : " + connector.listCanalLog());
System.out.println("getFile 2 : " + connector.canalLog(10));
System.out.println("listFile 3 : " + connector.listInstanceLog("example"));
System.out.println("getFile 3 : " + connector.instanceLog("example", "example.log", 10));
connector.disconnect();
}
}

View File

@@ -1,20 +0,0 @@
package com.alibaba.otter.canal.admin.jmx;
import org.junit.Test;
public class JMXConnectionTest {
@Test
public void testSimple() {
JMXConnection jmxConnection = new JMXConnection("127.0.0.1", 11113);
try {
CanalServerMXBean canalServerMXBean = jmxConnection.getCanalServerMXBean();
System.out.println(canalServerMXBean.getStatus());
} catch (Exception e) {
e.printStackTrace();
} finally {
jmxConnection.close();
}
}
}