|
@@ -1,19 +1,25 @@
|
|
package org.dbsyncer.listener.file;
|
|
package org.dbsyncer.listener.file;
|
|
|
|
|
|
import org.apache.commons.io.IOUtils;
|
|
import org.apache.commons.io.IOUtils;
|
|
|
|
+import org.dbsyncer.common.util.JsonUtil;
|
|
|
|
+import org.dbsyncer.common.util.NumberUtil;
|
|
import org.dbsyncer.common.util.RandomUtil;
|
|
import org.dbsyncer.common.util.RandomUtil;
|
|
import org.dbsyncer.connector.config.FileConfig;
|
|
import org.dbsyncer.connector.config.FileConfig;
|
|
import org.dbsyncer.connector.file.FileConnectorMapper;
|
|
import org.dbsyncer.connector.file.FileConnectorMapper;
|
|
|
|
+import org.dbsyncer.connector.model.FileSchema;
|
|
import org.dbsyncer.listener.AbstractExtractor;
|
|
import org.dbsyncer.listener.AbstractExtractor;
|
|
import org.dbsyncer.listener.ListenerException;
|
|
import org.dbsyncer.listener.ListenerException;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
+import org.springframework.util.Assert;
|
|
|
|
|
|
|
|
+import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.IOException;
|
|
import java.io.RandomAccessFile;
|
|
import java.io.RandomAccessFile;
|
|
import java.nio.file.*;
|
|
import java.nio.file.*;
|
|
import java.util.List;
|
|
import java.util.List;
|
|
-import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
+import java.util.Map;
|
|
|
|
+import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.locks.Lock;
|
|
import java.util.concurrent.locks.Lock;
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
|
|
|
|
@@ -31,6 +37,9 @@ public class FileExtractor extends AbstractExtractor {
|
|
private FileConnectorMapper connectorMapper;
|
|
private FileConnectorMapper connectorMapper;
|
|
private WatchService watchService;
|
|
private WatchService watchService;
|
|
private Worker worker;
|
|
private Worker worker;
|
|
|
|
+ private Map<String, RandomAccessFile> pipeline = new ConcurrentHashMap<>();
|
|
|
|
+ private static final byte[] buffer = new byte[4096];
|
|
|
|
+ private static final String POS_PREFIX = "pos_";
|
|
|
|
|
|
@Override
|
|
@Override
|
|
public void start() {
|
|
public void start() {
|
|
@@ -42,10 +51,11 @@ public class FileExtractor extends AbstractExtractor {
|
|
}
|
|
}
|
|
|
|
|
|
connectorMapper = (FileConnectorMapper) connectorFactory.connect(connectorConfig);
|
|
connectorMapper = (FileConnectorMapper) connectorFactory.connect(connectorConfig);
|
|
- FileConfig config = connectorMapper.getConfig();
|
|
|
|
|
|
+ final FileConfig config = connectorMapper.getConfig();
|
|
final String mapperCacheKey = connectorFactory.getConnector(connectorMapper).getConnectorMapperCacheKey(connectorConfig);
|
|
final String mapperCacheKey = connectorFactory.getConnector(connectorMapper).getConnectorMapperCacheKey(connectorConfig);
|
|
connected = true;
|
|
connected = true;
|
|
|
|
|
|
|
|
+ initPipeline(config.getFileDir(), config.getSchema());
|
|
watchService = FileSystems.getDefault().newWatchService();
|
|
watchService = FileSystems.getDefault().newWatchService();
|
|
Path p = Paths.get(config.getFileDir());
|
|
Path p = Paths.get(config.getFileDir());
|
|
p.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
|
|
p.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
|
|
@@ -56,54 +66,81 @@ public class FileExtractor extends AbstractExtractor {
|
|
worker.start();
|
|
worker.start();
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
logger.error("启动失败:{}", e.getMessage());
|
|
logger.error("启动失败:{}", e.getMessage());
|
|
|
|
+ closePipelineAndWatch();
|
|
throw new ListenerException(e);
|
|
throw new ListenerException(e);
|
|
} finally {
|
|
} finally {
|
|
connectLock.unlock();
|
|
connectLock.unlock();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ private void initPipeline(String fileDir, String schema) throws IOException {
|
|
|
|
+ List<FileSchema> fileSchemas = JsonUtil.jsonToArray(schema, FileSchema.class);
|
|
|
|
+ Assert.notEmpty(fileSchemas, "found not file schema.");
|
|
|
|
+ for (FileSchema fileSchema : fileSchemas) {
|
|
|
|
+ String file = fileDir.concat(fileSchema.getFileName());
|
|
|
|
+ Assert.isTrue(new File(file).exists(), String.format("found not file '%s'", file));
|
|
|
|
+
|
|
|
|
+ final RandomAccessFile raf = new RandomAccessFile(file, "r");
|
|
|
|
+ pipeline.put(fileSchema.getFileName(), raf);
|
|
|
|
+
|
|
|
|
+ final String filePosKey = getFilePosKey(fileSchema.getFileName());
|
|
|
|
+ if (snapshot.containsKey(filePosKey)) {
|
|
|
|
+ raf.seek(NumberUtil.toLong(snapshot.get(filePosKey), 0L));
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ raf.seek(raf.length());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
@Override
|
|
@Override
|
|
public void close() {
|
|
public void close() {
|
|
try {
|
|
try {
|
|
|
|
+ closePipelineAndWatch();
|
|
connected = false;
|
|
connected = false;
|
|
if (null != worker && !worker.isInterrupted()) {
|
|
if (null != worker && !worker.isInterrupted()) {
|
|
worker.interrupt();
|
|
worker.interrupt();
|
|
worker = null;
|
|
worker = null;
|
|
}
|
|
}
|
|
- if (null != watchService) {
|
|
|
|
- watchService.close();
|
|
|
|
- }
|
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
logger.error("关闭失败:{}", e.getMessage());
|
|
logger.error("关闭失败:{}", e.getMessage());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- private void parseEvent(String fileName) {
|
|
|
|
|
|
+ private void closePipelineAndWatch() {
|
|
|
|
+ try {
|
|
|
|
+ pipeline.values().forEach(raf -> IOUtils.closeQuietly(raf));
|
|
|
|
+ pipeline.clear();
|
|
|
|
|
|
|
|
+ if (null != watchService) {
|
|
|
|
+ watchService.close();
|
|
|
|
+ }
|
|
|
|
+ } catch (IOException ex) {
|
|
|
|
+ logger.error(ex.getMessage());
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- private void read(String file) {
|
|
|
|
- RandomAccessFile raf = null;
|
|
|
|
- byte[] buffer = new byte[4096];
|
|
|
|
- try {
|
|
|
|
- raf = new RandomAccessFile(file, "r");
|
|
|
|
- raf.seek(raf.length());
|
|
|
|
- logger.info("offset:{}", raf.getFilePointer());
|
|
|
|
|
|
+ private String getFilePosKey(String fileName) {
|
|
|
|
+ return POS_PREFIX.concat(fileName);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void parseEvent(String fileName) throws IOException {
|
|
|
|
+ if (pipeline.containsKey(fileName)) {
|
|
|
|
+ final RandomAccessFile raf = pipeline.get(fileName);
|
|
|
|
|
|
- while (true) {
|
|
|
|
- int len = raf.read(buffer);
|
|
|
|
- if (-1 != len) {
|
|
|
|
|
|
+ int len = 0;
|
|
|
|
+ while (-1 != len) {
|
|
|
|
+ len = raf.read(buffer);
|
|
|
|
+ if (0 < len) {
|
|
|
|
+ // TODO 解析 line
|
|
logger.info("offset:{}, len:{}", raf.getFilePointer(), len);
|
|
logger.info("offset:{}, len:{}", raf.getFilePointer(), len);
|
|
logger.info(new String(buffer, 1, len, "UTF-8"));
|
|
logger.info(new String(buffer, 1, len, "UTF-8"));
|
|
|
|
+ continue;
|
|
}
|
|
}
|
|
- TimeUnit.SECONDS.sleep(1);
|
|
|
|
}
|
|
}
|
|
- } catch (IOException e) {
|
|
|
|
- logger.error(e.getMessage());
|
|
|
|
- } catch (InterruptedException e) {
|
|
|
|
- logger.error(e.getMessage());
|
|
|
|
- } finally {
|
|
|
|
- IOUtils.closeQuietly(raf);
|
|
|
|
|
|
+
|
|
|
|
+ final String filePosKey = getFilePosKey(fileName);
|
|
|
|
+ snapshot.put(filePosKey, String.valueOf(raf.getFilePointer()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|