網頁

2019/4/6

Java 命令模式應用,替換IP位址

最近在工作中碰到一個情況,就是在建置開發環境的過程中,要將一個設定Server位址的properties檔中的IP位址全部改成自己本機的IP位址。

我原本這篇叫做策略模式應用,但我發現我搞錯了,這應該是命令模式才對。

之前都是用手動的方式來修改properties檔中各Server的IP,但因為數量很多可能稍微一忽略就出錯了,所以就寫了一支下面的jar檔來處理。

處理方式主要就是一行一行讀取properties檔的內容,然後用正則表示式(Regular Expression)找到並替換成自己的IP位址,然後輸出成新的properties檔。

比較麻煩的地方是要找到每個要替換的IP位置需要不同的regex規則,且替代的方式也不太一樣。

一開始直覺想到的方式是用陣列儲存每個regex規則然後loop,但問題是這樣的方式並無法得知目前所要搭配的替代方式。也就是說我需要一個集合可裝載key為regex,value為替代的方法實作。

最後想到利用類似策略模式(Strategy Pattern)的方式來解決。而那個裝載的集合就是一個列舉。

Main.java

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            fr = new FileReader(new File("server.properties")); // 要被替換IP位址的來源properties檔
            
            fw = new FileWriter("new_server.properties"); // 替換IP後的輸出properties檔
            
            BufferedReader br = new BufferedReader(fr);
            
            while(br.ready()) {
                String line = br.readLine(); // 一行一行讀取properties檔
                line = RegexEnum.findAndReplace(line); // 替換IP位址
                fw.write(line + System.lineSeparator()); // 將替換的內容寫出
            }
            fw.flush();
            fr.close();
        } catch (IOException e) {
            e.printStackTrace();
            
        }
    }

}

ReplaceStrategy.java

public interface ReplaceStrategy {
    
    public String replace(String s);

}

RegexEnum.java

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public enum RegexEnum implements ReplaceStrategy{
    GATEWAY_IP("^gateway_\\nip\\s*=") {
        @Override
        public String replace(String s) {
            return s.substring(0, s.indexOf('=') + 1) + " " + ip;
        }
    }
    GROUP_IP("^group_ip\\s*="){
        @Override
        public String replace(String s) {
            return s.substring(0, s.indexOf('=') + 1) + " group." + ip;
        }
    },
    TCP_HORN_HOST("^tcp_horn_host\\s*=") {
        @Override
        public String replace(String s) {
            return s.substring(0, s.indexOf('=') + 1) + " " + ip;
        }
    },
    DB_URL("^db_\\w*_url\\s*="){
        @Override
        public String replace(String s) {
            return s.replaceFirst("jdbc:mysql://.*/", "jdbc:mysql://"+ ip + "/");
        }
    },
    TOMCAT_HOST_1("^\\w*_tomcat_host1\\s*="){
        @Override
        public String replace(String s) {
            return s.substring(0, s.indexOf('=') + 1) + " " + ip;
        }
    },
    JEDIS_IP("^Jedis_\\w*_IP_Port\\s*="){
        @Override
        public String replace(String s) {
            return s.replaceFirst("redis://.*:", "redis://"+ ip + ":");
        }
    };

    private static String ip = null;
    
    static {
        try {
            ip = InetAddress.getLocalHost().getHostAddress(); // get local IPv4 addrss
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }
    
    private String regex;
    
    private RegexEnum(String regex) {
        this.regex = regex;
    }
    
    public static String findAndReplace(String line) {
        for (RegexEnum e : RegexEnum.values()) {
            Pattern p = Pattern.compile(e.getRegex());   
            Matcher m = p.matcher(line);
            if(m.find()) {
                return e.replace(line);
            }
        }
        return line;
    }

    public String getRegex() {
        return regex;
    }

}

以上的重點在RegexEnum的實作。RegexEnum實作RegexStrategy介面,所以每個RegexEnum有各自的正則表示式及對應的替代方法,每次從properties檔讀取一行內容都要遍歷每個RegexEnume規則,符合規則的那一行內容就用對應的替代方法來替代IP位址。


而在Java 8或許可以用Map<String, Function<String, String>>來裝載正則表示式與其要替代的方法,例如

    private static Map<String, Function<String, String>> regexes = new HashMap<>();
    private static String ip = null;

    static {
        ...
        regexes.put("^gateway_\\nip\\s*=", (s) -> s.substring(0, s.indexOf('=') + 1) + " " + ip);
        regexes.put("^group_ip\\s*=", (s) -> s.substring(0, s.indexOf('=') + 1) + " group." + ip);
        ...
    }   

然後再用forEach()之類的方式來loop每一行讀取的內容來替換。

但我對Java 8的lambda還不熟,所以就先寫到這了。


參考:

沒有留言:

張貼留言