NOTE: As from v1.3, this method throws an IOException if the last + * modified date of the file cannot be set. Also, as from v1.3 this method creates parent directories if they do not + * exist. + * + * @param file the File to touch + * @throws IOException If an I/O problem occurs + */ + public static void touch(File file) throws IOException { + FileUtils.touch(file); + } + + /** + * Creates a new empty file in the specified directory, using the given prefix and suffix strings to generate its + * name. The resulting {@code Path} is associated with the same {@code FileSystem} as the given directory. + * + *
The details as to how the name of the file is constructed is
+ * implementation dependent and therefore not specified. Where possible the {@code prefix} and {@code suffix} are
+ * used to construct candidate names in the same manner as the {@link File#createTempFile(String, String,
+ * File)} method.
+ *
+ * @param dir the path to directory in which to create the file
+ * @param prefix the prefix string to be used in generating the file's name; may be {@code null}
+ * @param suffix the suffix string to be used in generating the file's name; may be {@code null}, in which case
+ * "{@code .tmp}" is used
+ * @return the path to the newly created file that did not exist before this method was invoked
+ * @throws IllegalArgumentException if the prefix or suffix parameters cannot be used to generate a candidate
+ * file name
+ * @throws UnsupportedOperationException if the array contains an attribute that cannot be set atomically when
+ * creating the directory
+ * @throws IOException if an I/O error occurs or {@code dir} does not exist
+ * @throws SecurityException In the case of the default provider, and a security manager is installed,
+ * the {@link SecurityManager#checkWrite(String) checkWrite} method is invoked
+ * to check write access to the file.
+ */
+ public static File createTmpFile(String dir, String prefix, String suffix) throws IOException {
+ return Files.createTempFile(Paths.get(dir), prefix, suffix).toFile();
+ }
+
+ /**
+ * Creates an empty file in the default temporary-file directory, using the given prefix and suffix to generate its
+ * name. The resulting {@code Path} is associated with the default {@code FileSystem}.
+ *
+ * @param prefix the prefix string to be used in generating the file's name; may be {@code null}
+ * @param suffix the suffix string to be used in generating the file's name; may be {@code null}, in which case
+ * "{@code .tmp}" is used
+ * @return the path to the newly created file that did not exist before this method was invoked
+ * @throws IllegalArgumentException if the prefix or suffix parameters cannot be used to generate a candidate
+ * file name
+ * @throws UnsupportedOperationException if the array contains an attribute that cannot be set atomically when
+ * creating the directory
+ * @throws IOException if an I/O error occurs or the temporary-file directory does not exist
+ * @throws SecurityException In the case of the default provider, and a security manager is installed,
+ * the {@link SecurityManager#checkWrite(String) checkWrite} method is invoked
+ * to check write access to the file.
+ */
+ public static File createTmpFile(String prefix, String suffix) throws IOException {
+ return Files.createTempFile(prefix, suffix).toFile();
+ }
+
+ /**
+ * read file which under the path.
+ *
+ * @param path directory
+ * @param fileName filename
+ * @return content
+ */
+ public static String readFile(String path, String fileName) {
+ File file = openFile(path, fileName);
+ if (file.exists()) {
+ return readFile(file);
+ }
+ return null;
+ }
+
+ /**
+ * read file content by {@link InputStream}.
+ *
+ * @param is {@link InputStream}
+ * @return content
+ */
+ public static String readFile(InputStream is) {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
+ StringBuilder textBuilder = new StringBuilder();
+ String lineTxt = null;
+ while ((lineTxt = reader.readLine()) != null) {
+ textBuilder.append(lineTxt);
+ }
+ return textBuilder.toString();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * read this file content.
+ *
+ * @param file {@link File}
+ * @return content
+ */
+ public static String readFile(File file) {
+ try (FileChannel fileChannel = new FileInputStream(file).getChannel()) {
+ StringBuilder text = new StringBuilder();
+ ByteBuffer buffer = ByteBuffer.allocate(4096);
+ CharBuffer charBuffer = CharBuffer.allocate(4096);
+ while (fileChannel.read(buffer) != -1) {
+ buffer.flip();
+ DECODER.decode(buffer, charBuffer, false);
+ charBuffer.flip();
+ while (charBuffer.hasRemaining()) {
+ text.append(charBuffer.get());
+ }
+ buffer.clear();
+ charBuffer.clear();
+ }
+ return text.toString();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * read this file content then return bytes.
+ *
+ * @param file {@link File}
+ * @return content bytes
+ */
+ public static byte[] readFileBytes(File file) {
+ if (file.exists()) {
+ String result = readFile(file);
+ if (result != null) {
+ return ByteUtils.toBytes(result);
+ }
+ }
+ return null;
+ }
+
+ public static byte[] readFileBytes(String path, String fileName) {
+ File file = openFile(path, fileName);
+ return readFileBytes(file);
+ }
+
+ /**
+ * Writes the contents to the target file.
+ *
+ * @param file target file
+ * @param content content
+ * @param append write append mode
+ * @return write success
+ */
+ public static boolean writeFile(File file, byte[] content, boolean append) {
+ try (FileChannel fileChannel = new FileOutputStream(file, append).getChannel()) {
+ ByteBuffer buffer = ByteBuffer.wrap(content);
+ fileChannel.write(buffer);
+ return true;
+ } catch (IOException ioe) {
+ if (ioe.getMessage() != null) {
+ String errMsg = ioe.getMessage();
+ if (NO_SPACE_CN.equals(errMsg) || NO_SPACE_EN.equals(errMsg) || errMsg.contains(DISK_QUATA_CN) || errMsg
+ .contains(DISK_QUATA_EN)) {
+ LOGGER.warn("磁盘满,自杀退出");
+ System.exit(0);
+ }
+ }
+ }
+ return false;
+ }
+
+ public static void deleteQuietly(File file) {
+ Objects.requireNonNull(file, "file");
+ FileUtils.deleteQuietly(file);
+ }
+
+ public static void deleteQuietly(Path path) {
+ Objects.requireNonNull(path, "path");
+ FileUtils.deleteQuietly(path.toFile());
+ }
+
+ /**
+ * delete target file.
+ *
+ * @param path directory
+ * @param fileName filename
+ * @return delete success
+ */
+ public static boolean deleteFile(String path, String fileName) {
+ File file = Paths.get(path, fileName).toFile();
+ if (file.exists()) {
+ return file.delete();
+ }
+ return false;
+ }
+
+ public static void deleteDirectory(String path) throws IOException {
+ FileUtils.deleteDirectory(new File(path));
+ }
+
+ public static void forceMkdir(String path) throws IOException {
+ FileUtils.forceMkdir(new File(path));
+ }
+
+ public static void forceMkdir(File file) throws IOException {
+ FileUtils.forceMkdir(file);
+ }
+
+ public static void deleteDirThenMkdir(String path) throws IOException {
+ deleteDirectory(path);
+ forceMkdir(path);
+ }
+
+ public static void copyDirectory(File srcDir, File destDir) throws IOException {
+ FileUtils.copyDirectory(srcDir, destDir);
+ }
+
+ public static void copyFile(File src, File target) throws IOException {
+ FileUtils.copyFile(src, target);
+ }
+
+ public static File openFile(String path, String fileName) {
+ return openFile(path, fileName, false);
+ }
+
+ /**
+ * open file.
+ *
+ * @param path directory
+ * @param fileName filename
+ * @param rewrite if rewrite is true, will delete old file and create new one
+ * @return {@link File}
+ */
+ public static File openFile(String path, String fileName, boolean rewrite) {
+ File directory = new File(path);
+ boolean mkdirs = true;
+ if (!directory.exists()) {
+ mkdirs = directory.mkdirs();
+ }
+ if (!mkdirs) {
+ LOGGER.error("[DiskUtils] can't create directory");
+ return null;
+ }
+ File file = new File(path, fileName);
+ try {
+ boolean create = true;
+ if (!file.exists()) {
+ file.createNewFile();
+ }
+ if (file.exists()) {
+ if (rewrite) {
+ file.delete();
+ } else {
+ create = false;
+ }
+ }
+ if (create) {
+ file.createNewFile();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return file;
+ }
+
+ // copy from sofa-jraft
+
+ /**
+ * Compress a folder in a directory.
+ *
+ * @param rootDir directory
+ * @param sourceDir folder
+ * @param outputFile output file
+ * @param checksum checksum
+ * @throws IOException IOException
+ */
+ public static void compress(final String rootDir, final String sourceDir, final String outputFile,
+ final Checksum checksum) throws IOException {
+ try (final FileOutputStream fos = new FileOutputStream(outputFile);
+ final CheckedOutputStream cos = new CheckedOutputStream(fos, checksum);
+ final ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(cos))) {
+ compressDirectoryToZipFile(rootDir, sourceDir, zos);
+ zos.flush();
+ fos.getFD().sync();
+ }
+ }
+
+ // copy from sofa-jraft
+
+ private static void compressDirectoryToZipFile(final String rootDir, final String sourceDir,
+ final ZipOutputStream zos) throws IOException {
+ final String dir = Paths.get(rootDir, sourceDir).toString();
+ final File[] files = Objects.requireNonNull(new File(dir).listFiles(), "files");
+ for (final File file : files) {
+ final String child = Paths.get(sourceDir, file.getName()).toString();
+ if (file.isDirectory()) {
+ compressDirectoryToZipFile(rootDir, child, zos);
+ } else {
+ try (final FileInputStream fis = new FileInputStream(file);
+ final BufferedInputStream bis = new BufferedInputStream(fis)) {
+ compressIntoZipFile(child, bis, zos);
+ }
+ }
+ }
+ }
+
+ /**
+ * Compress an input stream to zip file.
+ *
+ * @param childName child name in zip file
+ * @param inputStream input stream needed compress
+ * @param outputFile output file
+ * @param checksum check sum
+ * @throws IOException IOException during compress
+ */
+ public static void compressIntoZipFile(final String childName, final InputStream inputStream,
+ final String outputFile, final Checksum checksum) throws IOException {
+ try (final FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
+ final CheckedOutputStream checkedOutputStream = new CheckedOutputStream(fileOutputStream, checksum);
+ final ZipOutputStream zipStream = new ZipOutputStream(new BufferedOutputStream(checkedOutputStream))) {
+ compressIntoZipFile(childName, inputStream, zipStream);
+ zipStream.flush();
+ fileOutputStream.getFD().sync();
+ }
+ }
+
+ private static void compressIntoZipFile(final String childName, final InputStream inputStream,
+ final ZipOutputStream zipOutputStream) throws IOException {
+ zipOutputStream.putNextEntry(new ZipEntry(childName));
+ IOUtils.copy(inputStream, zipOutputStream);
+ }
+
+ // copy from sofa-jraft
+
+ /**
+ * Unzip the target file to the specified folder.
+ *
+ * @param sourceFile target file
+ * @param outputDir specified folder
+ * @param checksum checksum
+ * @throws IOException IOException
+ */
+ public static void decompress(final String sourceFile, final String outputDir, final Checksum checksum)
+ throws IOException {
+ try (final FileInputStream fis = new FileInputStream(sourceFile);
+ final CheckedInputStream cis = new CheckedInputStream(fis, checksum);
+ final ZipInputStream zis = new ZipInputStream(new BufferedInputStream(cis))) {
+ ZipEntry entry;
+ while ((entry = zis.getNextEntry()) != null) {
+ final String fileName = entry.getName();
+ final File entryFile = new File(Paths.get(outputDir, fileName).toString());
+ FileUtils.forceMkdir(entryFile.getParentFile());
+ try (final FileOutputStream fos = new FileOutputStream(entryFile);
+ final BufferedOutputStream bos = new BufferedOutputStream(fos)) {
+ IOUtils.copy(zis, bos);
+ bos.flush();
+ fos.getFD().sync();
+ }
+ }
+ // Continue to read all remaining bytes(extra metadata of ZipEntry) directly from the checked stream,
+ // Otherwise, the checksum value maybe unexpected.
+ //
+ // See https://coderanch.com/t/279175/java/ZipInputStream
+ IOUtils.copy(cis, NullOutputStream.NULL_OUTPUT_STREAM);
+ }
+ }
+
+ /**
+ * Unzip the target file to byte array.
+ *
+ * @param sourceFile target file
+ * @param checksum checksum
+ * @return decompress byte array
+ * @throws IOException IOException during decompress
+ */
+ public static byte[] decompress(final String sourceFile, final Checksum checksum) throws IOException {
+ byte[] result;
+ try (final FileInputStream fis = new FileInputStream(sourceFile);
+ final CheckedInputStream cis = new CheckedInputStream(fis, checksum);
+ final ZipInputStream zis = new ZipInputStream(new BufferedInputStream(cis));
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream(1024)) {
+ while (zis.getNextEntry() != null) {
+ IOUtils.copy(zis, bos);
+ bos.flush();
+ }
+ IOUtils.copy(cis, NullOutputStream.NULL_OUTPUT_STREAM);
+ result = bos.toByteArray();
+ }
+ return result;
+ }
+
+ /**
+ * Returns an Iterator for the lines in a File
.
+ *
+ * This method opens an InputStream
for the file. When you have finished with the iterator you should
+ * close the stream to free internal resources. This can be done by calling the {@link
+ * org.apache.commons.io.LineIterator#close()} or {@link org.apache.commons.io.LineIterator#closeQuietly(org.apache.commons.io.LineIterator)}
+ * method.
+ *
+ * LineIterator it = FileUtils.lineIterator(file, "UTF-8"); + * try { + * while (it.hasNext()) { + * String line = it.nextLine(); + * /// do something with line + * } + * } finally { + * LineIterator.closeQuietly(iterator); + * } + *+ *
+ * If an exception occurs during the creation of the iterator, the underlying stream is closed. + *
+ * + * @param file the file to open for input, must not benull
+ * @param encoding the encoding to use, null
means platform default
+ * @return an Iterator of the lines in the file, never null
+ * @throws IOException in case of an I/O error (file closed)
+ * @since 1.2
+ */
+ public static LineIterator lineIterator(File file, String encoding) throws IOException {
+ return new LineIterator(FileUtils.lineIterator(file, encoding));
+ }
+
+ /**
+ * Returns an Iterator for the lines in a File
using the default encoding for the VM.
+ *
+ * @param file the file to open for input, must not be null
+ * @return an Iterator of the lines in the file, never null
+ * @throws IOException in case of an I/O error (file closed)
+ * @see #lineIterator(File, String)
+ * @since 1.3
+ */
+ public static LineIterator lineIterator(File file) throws IOException {
+ return new LineIterator(FileUtils.lineIterator(file, null));
+ }
+
+ public static class LineIterator implements AutoCloseable {
+
+ private final org.apache.commons.io.LineIterator target;
+
+ /**
+ * Constructs an iterator of the lines for a Reader
.
+ *
+ * @param target {@link org.apache.commons.io.LineIterator}
+ */
+ LineIterator(org.apache.commons.io.LineIterator target) {
+ this.target = target;
+ }
+
+ public boolean hasNext() {
+ return target.hasNext();
+ }
+
+ public String next() {
+ return target.next();
+ }
+
+ public String nextLine() {
+ return target.nextLine();
+ }
+
+ @Override
+ public void close() throws IOException {
+ target.close();
+ }
+
+ public void remove() {
+ target.remove();
+ }
+
+ public void forEachRemaining(Consumer super String> action) {
+ target.forEachRemaining(action);
+ }
+ }
+
+}
diff --git a/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/LocalDiskRuleStorage.java b/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/LocalDiskRuleStorage.java
index cfacfa5fae1..dcb1fc64018 100644
--- a/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/LocalDiskRuleStorage.java
+++ b/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/LocalDiskRuleStorage.java
@@ -18,8 +18,6 @@
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.plugin.control.Loggers;
-import com.alibaba.nacos.sys.env.EnvUtil;
-import com.alibaba.nacos.sys.utils.DiskUtils;
import org.slf4j.Logger;
import java.io.File;
@@ -38,16 +36,30 @@ public class LocalDiskRuleStorage implements RuleStorage {
private static final Logger LOGGER = Loggers.CONTROL;
+ private String localRruleBaseDir = defaultBaseDir();
+
private File checkTpsBaseDir() {
- File baseDir = new File(EnvUtil.getNacosHome(), "data" + File.separator + "tps" + File.separator);
+ File baseDir = new File(localRruleBaseDir, "data" + File.separator + "tps" + File.separator);
if (!baseDir.exists()) {
baseDir.mkdirs();
}
return baseDir;
}
+ public String getLocalRruleBaseDir() {
+ return localRruleBaseDir;
+ }
+
+ public void setLocalRruleBaseDir(String localRruleBaseDir) {
+ this.localRruleBaseDir = localRruleBaseDir;
+ }
+
+ private static String defaultBaseDir() {
+ return LocalDiskRuleStorage.class.getResource("/").getPath();
+ }
+
private File getConnectionRuleFile() {
- File baseDir = new File(EnvUtil.getNacosHome(), "data" + File.separator + "connection" + File.separator);
+ File baseDir = new File(localRruleBaseDir, "data" + File.separator + "connection" + File.separator);
if (!baseDir.exists()) {
baseDir.mkdir();
}
diff --git a/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/RuleStorageProxy.java b/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/RuleStorageProxy.java
index 4bf335dbf59..77d60452071 100644
--- a/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/RuleStorageProxy.java
+++ b/plugin/control/src/main/java/com/alibaba/nacos/plugin/control/ruleactivator/RuleStorageProxy.java
@@ -35,11 +35,14 @@ public class RuleStorageProxy {
private static final RuleStorageProxy INSTANCE = new RuleStorageProxy();
- private LocalDiskRuleStorage localDiskRuleStorage = new LocalDiskRuleStorage();
+ private LocalDiskRuleStorage localDiskRuleStorage = null;
private ExternalRuleStorage externalRuleStorage = null;
+ ControlRuleChangeActivator controlRuleChangeActivator = null;
+
public RuleStorageProxy() {
+
Collection