java實現svn,svnkit框架的簡單應用

SvnKit
地址:https://svnkit.com/download.php

項目需要做了個簡單的demo,可以進行基礎操作。demo可以運行,但是更多高級更多實現需要自己在擴展。因爲這個項目已經進入Thread.sleep :( ,開展新項目了 :(

功能
1.實現了幾個基礎操作
2.提供了日誌操作

項目結構

這裏寫圖片描述

首先我們先創建3個對象來爲後面服務

package com.svn.model;
/**
 * Svn賬號信息
 * 
 * @author Allen 
 * @date 2016年8月8日
 */
public class SvnAccountPojo implements java.io.Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String svnAccount;// svn賬號
    private String svnPassword;// svn密碼

    protected SvnAccountPojo() {
        // TODO Auto-generated constructor stub
    }

    public SvnAccountPojo(String svnAccount, String svnPassword) {
        super();
        this.svnAccount = svnAccount;
        this.svnPassword = svnPassword;
    }

    public String getSvnAccount() {
        return svnAccount;
    }

    public void setSvnAccount(String svnAccount) {
        this.svnAccount = svnAccount;
    }

    public String getSvnPassword() {
        return svnPassword;
    }

    public void setSvnPassword(String svnPassword) {
        this.svnPassword = svnPassword;
    }

}
package com.svn.model;

/**
 * Svn鏈接狀態信息
 * 
 * @author Allen 
 * @date 2016年8月8日
 */
public class SvnLinkPojo extends SvnAccountPojo {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String repoPath;// 庫鏈接路徑

    public SvnLinkPojo(String repoPath, String svnAccount, String svnPassword) {
        super(svnAccount, svnPassword);
        this.repoPath = repoPath;
    }

    public SvnLinkPojo(String svnAccount, String svnPassword) {
        super(svnAccount, svnPassword);
    }

    public String getRepoPath() {
        return repoPath;
    }

    public void setRepoPath(String repoPath) {
        this.repoPath = repoPath;
    }

}
package com.svn.model;

import java.util.Date;

/**
 * SVN資源庫對象
 * 
 * @author Allen 
 * @date 2016年8月8日
 */
public class SvnRepoPojo implements java.io.Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String commitMessage; // 提交信息
    private Date date; // 提交日期
    private String kind; // 提交方式 dir目錄 file文件 none空 unknown 未知
    private String name;// 目錄名
    private String repositoryRoot; // 資源庫路徑
    private long revision; // 提交的svn版本號
    private long size; // 提交的文件數
    private String url; // 更變的目錄地址
    private String author;// 作者
    private String state;// 狀態

    public String getCommitMessage() {
        return commitMessage;
    }

    public void setCommitMessage(String commitMessage) {
        this.commitMessage = commitMessage;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public String getKind() {
        return kind;
    }

    public void setKind(String kind) {
        this.kind = kind;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getRepositoryRoot() {
        return repositoryRoot;
    }

    public void setRepositoryRoot(String repositoryRoot) {
        this.repositoryRoot = repositoryRoot;
    }

    public long getRevision() {
        return revision;
    }

    public void setRevision(long revision) {
        this.revision = revision;
    }

    public long getSize() {
        return size;
    }

    public void setSize(long size) {
        this.size = size;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

}

創建svn基礎服務接口

package com.svn.inf.service;

/**
 * svn主服務創建
 * 
 * @author Allen
 * @date 2016年8月8日
 */
public interface ISvnService {

    /**
     * 創建SNV版本庫服務
     * 
     * @author Allen
     * @date 2016年8月11日
     */
    public void createSVNRepository();

    /**
     * 關閉版本庫容器,便於刷新重連等
     * 
     * @author Allen
     * @date 2016年8月11日
     */
    public void closeRepo();

    /**
     * 創建svn客戶操作服務
     * 
     * @author Allen
     * @date 2016年8月11日
     */
    public void createSVNClientManager();
}

建立主功能接口

package com.svn.inf;

import java.io.File;
import java.util.List;

import com.svn.inf.service.ISvnDbLog;
import com.svn.inf.service.ISvnService;
import com.svn.model.SvnRepoPojo;

/**
 * svn操作大全
 * 
 * @author Allen
 * @date 2016年8月8日
 */
public interface ISvn extends ISvnService {

    /**
     * 獲取目標路徑下版本庫數據信息
     * 
     * @param openPath
     *            需要查看的版本庫路徑
     * @return 版本庫列表 {@link SvnRepoPojo}
     * @author Allen
     * @date 2016年8月11日
     */
    public List<SvnRepoPojo> getRepoCatalog(String openPath);

    /**
     * 檢出到目錄
     * 
     * @param checkUrl
     *            檢出目標URL
     * @param savePath
     *            檢出到本地路徑
     * @return true|false
     * @author Allen
     * @date 2016年8月11日
     */
    public boolean checkOut(String checkUrl, String savePath);

    /**
     * 添加到版本庫
     * 
     * @see 先添加文件夾再添加文件
     * @param paths
     *            提交文件路徑
     * @param message
     *            提交信息
     * @param uLocks
     *            是否解鎖
     * @param isvnLog
     *            數據持久化接口 {@link ISvnDbLog}
     * @return trun|false
     * @author Allen
     * @date 2016年8月11日
     */
    public <T> boolean add(String[] paths, String message, boolean uLocks, ISvnDbLog<? extends T> isvnLog);

    /**
     * 提交到版本庫(所有寫操作已在內部調用過COMMIT,自行調用則需要手動同步到DbLog)
     * 
     * @param files
     *            提交的文件路徑
     * @param message
     *            提交信息
     * @param uLocks
     *            是否解鎖
     * @return 返回提交後版本號-1爲提交失敗
     * @author Allen
     * @date 2016年8月11日
     */
    public Long commit(File[] files, String message, boolean uLocks);

    /**
     * 刪除到版本庫
     * 
     * @see 先刪除文件再刪除文件夾
     * @param paths
     *            提交文件路徑
     * @param localDelete
     *            <ul>
     *            <li>如果是true則在本地也刪除此文件,false則只刪除版本庫中的此文件</li>
     *            <li>刪除實體文件時要注意</li>
     *            <li>刪除文件夾時其目錄下所有內容都要提交到<b>參數paths</b>中,否則無法刪除實體文件</li>
     *            </ul>
     * @param message
     *            提交內容解釋
     * @param uLock
     *            是否解鎖
     * @param isvnLog
     *            數據持久化接口 {@link ISvnDbLog}
     * @return true|false
     * @author Allen
     * @date 2016年8月11日
     */
    public <T> boolean delete(String[] paths, boolean localDelete, String message, boolean uLock, ISvnDbLog<? extends T> isvnLog);

    /**
     * 更新到版本庫
     * 
     * @param path
     *            要更新的文件目錄
     * @param message
     *            提交內容解釋
     * @param uLock
     *            是否解鎖
     * @param isvnLog
     *            數據持久化接口 {@link ISvnDbLog}
     * @return true|false
     * @author Allen
     * @date 2016年8月11日
     */
    public <T> boolean update(String path, String message, boolean uLock, ISvnDbLog<? super T> isvnLog);

    /**
     * 比對目錄下內容信息
     * 
     * @see 返回delete,update文件列表
     * @param file
     *            待比對的目標文件路徑
     * @return 返回有差異的文件路徑否則爲null
     * @author Allen
     * @date 2016年8月11日
     */
    public List<String> diffPath(File file);

    /**
     * 清理目錄
     * 
     * @param file
     *            待清理目錄
     * @return true|false
     * @author Allen
     * @date 2016年8月17日
     */
    public boolean cleanUp(File file);

    public boolean doLock();

    public boolean unLock();
}

當然我們還有通用的工具接口

package com.svn.inf.service;

import java.io.File;
import java.util.List;

/**
 * Svn功能組件
 * 
 * @author Allen
 * @date 2016年8月12日
 *
 */
public interface ISvnCommon {
    /**
     * 組裝file[]分離文件與文件夾
     * 
     * @param files
     *            待重組的文件列表
     * @return 文件容器組</br>index:0 文件夾</br> index:1 文件
     * @author Allen
     * @date 2016年8月11日
     */
    public List<List<File>> bindFile(File[] files);

    /**
     * 排序</br> 父級 ->子級
     * 
     * @param files
     *            待排序文件數組
     * @return 排序後文件數組
     * @author Allen
     * @date 2016年8月11日
     */
    public File[] sortF_S(File[] files);

    /**
     * 排序</br> 子級 ->父級
     * 
     * @param files
     *            待排序文件數組
     * @return 排序後文件數組
     * @author Allen
     * @date 2016年8月11日
     */
    public File[] sortS_F(File[] files);

    /**
     * 檢查文件路徑信息並組裝到文件容器
     * 
     * @param paths
     *            待組裝文件路徑
     * @return 文件數組
     * @throws Exception
     *             文件路徑中有尋找不到的地址
     * @author Allen
     * @date 2016年8月11日
     */
    public File[] checkFilePaths(String[] paths) throws Exception;
}

爲了滿足上級要求我們提供了db接口

package com.svn.inf.service;

import java.io.File;
import java.util.Date;
import java.util.List;

import com.svn.conf.SvnConfig;

/**
 * 記錄svn操作人
 * 
 * @author Allen
 * @date 2016年8月8日
 */
public interface ISvnDbLog<T> {
    /**
     * 添加日誌
     * 
     * @param name
     *            操作人賬號
     * @param dbType
     *            數據類型{@link SvnConfig} 
     * @param versionId
     *            版本號
     * @param files
     *            操作的文件組
     * @return true|false
     * @author Allen
     * @date 2016年8月11日
     */
    public boolean addLog(String name, SvnConfig dbType, long versionId, File[] files);

    /**
     * 獲取日誌
     * 
     * @param name
     *            操作人賬號
     * @param startTime
     *            日誌開始時間
     * @param endTime
     *            日誌結束時間
     * @return T 類型列表
     * @author Allen
     * @date 2016年8月11日
     */
    public List<? super T> getLog(String name, Date startTime, Date endTime);
}

到上面爲止我們的接口全部做好。這是一個java項目是希望做爲一個工具jar的存在,demo中只做了一套基礎的實現,按照這個思路根據不同需求可以慢慢擴展


工具類
其實這裏自己寫了個簡單的錯誤匹配,主要是針對svnkit的errorCode轉義,當然也可以用log4j等開源日誌。不過畢竟是demo麼 :)

package com.svn.tools;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

/**
 * 控制檯輸出log信息
 * 
 * @author Allen
 * @date 2016年8月8日
 */
public class ConsoleLog {
    protected boolean log = false;
    /**
     * 錯誤編碼
     */
    private String[] errorCode = { "E155010", "E200005", "E204899", "E200009", "E155015","E155004" };
    /**
     * 編碼解釋
     */
    private String[] errorInfo = { "找不到文件或文件夾,其所在目錄未被納入版本控制的", "文件沒有被納入版本控制", "無法訪問的目錄", "文件沒有被納入版本控制", "svn衝突!","文件被鎖定,可能是因爲上次操作意外中斷導致,請執行cleanup" };

    /**
     * 輸出log到console
     * 
     * @param msg
     *            輸出信息
     */
    protected void log(String msg, String... content) {
        if (log) {
            StackTraceElement stack[] = (new Throwable()).getStackTrace();
            String time = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
            System.out.println(new StringBuffer("[").append(time).append("]").append(stack[1].getClassName()).append(".").append(stack[1].getMethodName()).append("  line:")
                    .append(stack[1].getLineNumber()));
            System.out.println(new StringBuffer("[").append(time).append("]").append(msg));
        }
    }

    protected void log(Exception e) {
        StringBuffer sbf = new StringBuffer("【SVN-ERROR】");
        for (int i = 0; i < errorCode.length; i++) {
            if (e.getMessage().indexOf(errorCode[i]) != -1)
                this.log(sbf.append(errorInfo[i]).toString());
        }
    }

}

這裏主要是輸出文本比對時差異信息
說實話版本衝突這裏我svn是可以對列表進行更新或寫操作的時候,批量返回錯誤信息,而svn提供的cmd入口及svnkit對其cmd命令的實現,都是提供的單條問題拋出,譬如我update一個文件目錄,在第3,第5個文件會出現衝突,則接口在第三個文件直接就exception了,無法往下進行了。當然你可以遞歸從第四條再更新執行,最後把結果合併顯示 :)

package com.svn.tools;

import java.io.IOException;
import java.io.OutputStream;
import java.util.List;

/**
 * Output輸出到字符串
 * 
 * @author Allen
 * @date 2016年8月11日
 *
 */
public final class StringOutputSteam extends OutputStream {

    public List<String> s;

    public StringOutputSteam(List<String> s) {
        this.s = s;
    }

    @Override
    public void write(int b) throws IOException {
    }

    @Override
    public void write(byte b[], int off, int len) throws IOException {
        String temp = new String(b);
        String[] temps = temp.split("\r\n");
        //System.out.println(temp);
        boolean flag = false;
        for (int i = 0; i < temps.length; i++) {
            // 根據svn命令返回Index: xxxx(path)獲取所有包含delete update的path獲取
            if (temps[i].split(" ")[0].equalsIgnoreCase("Index:")) {
                String p = temps[i].substring(7, temps[i].length());
                if (s.indexOf(p) == -1) {
                    s.add(p);
                    flag = true;
                    continue;
                }
            }
            // 以Index:爲開始點來判定svn diff返回的Log信息
            // LogFormat:
            // Index: path
            // =============================
            // xxxxxxx
            // Index: path
            // =============================
            // xxxxxxx
            if (flag) {
                String state = "";
                for (int j = i; j < temps.length; j++) {
                    if (temps[j].split(" ")[0].equalsIgnoreCase("Index:"))
                        break;
                    if (temps[j].split(" ")[0].equalsIgnoreCase("---"))
                        state = "衝突";
                    else if (state.equals(""))
                        state = "刪除";
                } 
                s.add(state);
                flag = false;
            }
        }
    }
}

這裏是自定義的錯誤信息

package com.svn.conf;

/**
 * 錯誤信息庫
 * 
 * @author Allen
 * @date 2016年8月8日
 *
 */
public final class ErrorVal {

    public final static String SVNRepository_is_null = "版本庫未被創建";
    public final static String SVNClientManager = "";
    public final static String SVNRepository_is_having = "版本庫已存在 ,可以調用closeRepo進行[庫服務清理]";
    public final static String ISVNAuthenticationManager_is_null = "身份驗證器還未被創建";
    public final static String Path_no_having = "目標地址路徑不存在";
    public final static String Url_no_having = "目標庫路徑 不存在";
    public final static String File_not_exist_repo = "文件所在目錄沒有被svn版本控制";
    public final static String SvnConfig_is_null = "錯誤的配置";
    public final static String Commit_error = "提交錯誤[==請確認文件是否已有其他版本操作,請檢查文件是否不存在==]";
    public final static String AddDbLog_error = "添加到數據日誌錯誤";
    public final static String File_Repo_no_having = "文件不存在版本庫中";
    public final static String Repo_Status_error = "版本狀態錯誤";
    public final static String Update_no_change = "沒有變化的更新";
    public final static String File_is_not_directory = "File不是一個目錄";
}

這裏不僅爲小工廠提供了選項,還提供了對抽象db日誌的數據設置

package com.svn.conf;

/**
 * 系統參數設置
 * 
 * @see 外部通過<b>SvnConfig.xx</b>來一個<b>SvnConfig</b>類型傳入不同的配置
 * @see 外部通過<b>SvnConfig.xx.get</b>來以<b>String</b>類型得到不同的配置
 * @author Allen
 * @date 2016年8月8日
 * 
 */
public class SvnConfig {
    /**
     * 選擇非日誌模式
     */
    public static final SvnConfig normal = new SvnConfig("normal");
    /**
     * 選擇console日誌模式
     */
    public static final SvnConfig log = new SvnConfig("log");
    /**
     * 數據存儲類型Add
     */
    public static final SvnConfig add = new SvnConfig("add");
    /**
     * 數據存儲類型Update
     */
    public static final SvnConfig update = new SvnConfig("update");
    /**
     * 數據存儲類型Delete
     */
    public static final SvnConfig delete = new SvnConfig("delete");
    private String val;

    public String getVal() {
        return val;
    }

    private SvnConfig() {
    }

    private SvnConfig(String val) {
        this.val = val;

    }
}

實現類來了。。。攔不住的implements
提供了對通用工具接口的實現,爲什麼要那麼複雜呢。因爲例如add接口,必須要先提交最底層的文件夾目錄從深層提交到高層,然後再提交其文件,以此類推,說白了就是你沒有文件夾時無法建立文件的。

package com.svn.impl.service;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.svn.conf.ErrorVal;
import com.svn.inf.service.ISvnCommon;
import com.svn.tools.ConsoleLog;

/**
 * {@link ISvnCommon}
 * 
 * @author Allen
 * @date 2016年8月12日
 *
 */
public class SvnCommonImpl extends ConsoleLog implements ISvnCommon {

    @Override
    public List<List<File>> bindFile(File[] files) {
        List<List<File>> fileList = new ArrayList<List<File>>(1);
        fileList.add(new ArrayList<File>());
        fileList.add(new ArrayList<File>());
        for (File f : files) {
            if (f.isDirectory())
                // 0是文件目錄,先提交文件目錄讓再提交文件
                fileList.get(0).add(f);
            else
                fileList.get(1).add(f);
        }
        for (int i = 0; i < fileList.size(); i++) {
            if (fileList.get(i).size() == 0)
                fileList.remove(i);
        }
        return fileList;
    }

    @Override
    public File[] sortF_S(File[] files) {
        Arrays.sort(files);
        return files;
    }

    @Override
    public File[] sortS_F(File[] files) {
        files = sortF_S(files);
        File[] f = new File[files.length];
        for (int i = 0; i < files.length;) {
            f[i] = files[files.length - (++i)];
        }
        return f;
    }

    @Override
    public File[] checkFilePaths(String[] paths) throws Exception {
        if (paths == null | paths.length == 0)
            throw new Exception(ErrorVal.Path_no_having);
        File[] files = new File[paths.length];
        for (int i = 0; i < paths.length; i++) {
            if (!(files[i] = new File(paths[i])).exists())
                throw new Exception(ErrorVal.Path_no_having);
        }
        return files;
    }

}

服務接口實現類,快樂的建立庫容器
因爲整個項目保持開閉原則,讓你用的你隨便用,不讓你用的你就老實點:)
一次創建整個實例都能在開閉原則下快樂的使用各種創建好的實例/各種服務類

package com.svn.impl.service;

import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

import com.svn.conf.ErrorVal;
import com.svn.inf.service.ISvnService;

/**
 * {@link ISvnService}
 * 
 * @author Allen
 * @date 2016年8月8日
 */
public class SvnServiceImpl extends SvnCommonImpl implements ISvnService {

    protected String svnAccount; // svn賬號
    protected String svnPassword;// svn密碼
    protected String svnRepoPath;// svn版本庫根目錄
    protected boolean logStatus = false;// 日誌狀態
    protected SVNRepository repository = null;// 版本庫服務
    protected ISVNAuthenticationManager authManager;// 身份驗證器
    protected SVNClientManager clientManager;// svn客戶操作服務

    /**
     * 
     * @param account
     *            賬號
     * @param password
     *            密碼
     * @param logStatus
     *            是否開啓日誌狀態(默認false)
     * @param repoPath
     *            svn庫根目錄
     * 
     */
    public SvnServiceImpl(String account, String password, boolean logStatus, String repoPath) {
        this.svnAccount = account;
        this.svnPassword = password;
        this.svnRepoPath = repoPath;
        super.log = logStatus;
    }

    @Override
    public void createSVNRepository() {
        try {
            if (repository != null)
                throw new Exception(ErrorVal.SVNRepository_is_having);
            // 創建庫連接
            SVNRepository repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(this.svnRepoPath));
            super.log("創建版本庫連接");
            // 身份驗證
            this.authManager = SVNWCUtil.createDefaultAuthenticationManager(this.svnAccount, this.svnPassword.toCharArray());
            super.log("創建身份驗證");
            // 創建身份驗證管理器
            repository.setAuthenticationManager(authManager);
            this.repository = repository;
            super.log("設置版本庫身份驗證");
        } catch (SVNException e) {
            super.log(e);
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void closeRepo() {
        if (repository == null)
            try {
                throw new NullPointerException(ErrorVal.SVNRepository_is_null);
            } catch (Exception e) {
                e.printStackTrace();
            }
        else {
            repository.closeSession();
            repository = null;
            super.log("關閉版本庫");
        }
    }

    @Override
    public void createSVNClientManager() {
        if (authManager == null)
            try {
                throw new NullPointerException(ErrorVal.ISVNAuthenticationManager_is_null);
            } catch (Exception e) {
                e.printStackTrace();
            }
        clientManager = SVNClientManager.newInstance(SVNWCUtil.createDefaultOptions(true), authManager);
        super.log("創建svn客戶操作服務");
    }

}

其實這裏本來還想寫個池服務。。不過時間關係就沒寫。畢竟demo沒有後續跟進計劃了。以後有機會再補

package com.svn.impl.service;

import com.svn.impl.service.SvnCommonImpl;
import com.svn.inf.service.ISvnService;

/**
 * {@link ISvnService} 池的實現
 * 
 * @author Allen
 * @date 2016年8月8日
 */
public class SvnServicePoolImpl extends SvnCommonImpl implements ISvnService {

    @Override
    public void createSVNRepository() {
        // TODO Auto-generated method stub

    }

    @Override
    public void closeRepo() {
        // TODO Auto-generated method stub

    }

    @Override
    public void createSVNClientManager() {
        // TODO Auto-generated method stub

    }
}

這裏..這裏就是業務實現了,其實就是對svnkit進行了一個封裝,讓他更符合項目要求

package com.svn.impl;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.SVNDiffClient;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNStatusType;
import org.tmatesoft.svn.core.wc.SVNUpdateClient;

import com.svn.conf.ErrorVal;
import com.svn.conf.SvnConfig;
import com.svn.impl.service.SvnServiceImpl;
import com.svn.inf.ISvn;
import com.svn.inf.service.ISvnDbLog;
import com.svn.inf.service.ISvnService;
import com.svn.model.SvnRepoPojo;
import com.svn.tools.StringOutputSteam;

/**
 * 
 * {@link ISvnService}
 * 
 * @author Allen
 * @date 2016年8月8日
 *
 */
public class SvnBaseImpl extends SvnServiceImpl implements ISvn {

    public SvnBaseImpl(String account, String password, boolean logStatus, String repoPath) {
        super(account, password, logStatus, repoPath);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<SvnRepoPojo> getRepoCatalog(String openPath) {
        try {
            if (repository == null)
                throw new Exception(ErrorVal.SVNRepository_is_null);
            Collection<SVNDirEntry> entries = repository.getDir(openPath, -1, null, (Collection<SVNDirEntry>) null);
            List<SvnRepoPojo> svns = new ArrayList<SvnRepoPojo>();
            Iterator<SVNDirEntry> it = entries.iterator();
            while (it.hasNext()) {
                SVNDirEntry entry = it.next();
                SvnRepoPojo svn = new SvnRepoPojo();
                svn.setCommitMessage(entry.getCommitMessage());
                svn.setDate(entry.getDate());
                svn.setKind(entry.getKind().toString());
                svn.setName(entry.getName());
                svn.setRepositoryRoot(entry.getRepositoryRoot().toString());
                svn.setRevision(entry.getRevision());
                svn.setSize(entry.getSize() / 1024);
                svn.setUrl(openPath.equals("") ? new StringBuffer("/").append(entry.getName()).toString() : new StringBuffer(openPath).append("/").append(entry.getName()).toString());
                svn.setAuthor(entry.getAuthor());
                svn.setState(svn.getKind() == "file" ? null : "closed");
                svns.add(svn);
            }
            super.log("獲得版本庫文件信息");
            return svns;
        } catch (SVNException e) {
            super.log(e);
            e.printStackTrace();
            return null;
        } catch (Exception e) {
            super.log(e);
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public boolean checkOut(String checkUrl, String savePath) {
        SVNUpdateClient updateClient = clientManager.getUpdateClient();
        updateClient.setIgnoreExternals(false);
        try {
            if (savePath == null || savePath.trim().equals(""))
                throw new Exception(ErrorVal.Path_no_having);
            else if (checkUrl == null || checkUrl.trim().equals(""))
                throw new Exception(ErrorVal.Url_no_having);
            File save = new File(savePath);
            if (!save.isDirectory())
                save.mkdir();
            updateClient.doCheckout(SVNURL.parseURIEncoded(checkUrl), save, SVNRevision.HEAD, SVNRevision.HEAD, SVNDepth.INFINITY, false);
            super.log("檢出版本庫信息");
            return true;
        } catch (SVNException e) {
            super.log(e);
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 此實現自動對Add後的數據進行提交
     */
    @Override
    public <T> boolean add(String[] paths, String message, boolean uLocks, ISvnDbLog<? extends T> isvnLog) {
        try {
            File[] files = checkFilePaths(paths);
            files = sortF_S(files);
            SVNStatus status;
            List<File> targetList = new ArrayList<File>();
            List<List<File>> fileList = bindFile(files);
            for (int i = 0; i < fileList.size(); i++) {
                for (File f : fileList.get(i)) {
                    if ((status = clientManager.getStatusClient().doStatus(f, true, true)) != null && status.getContentsStatus() != SVNStatusType.STATUS_UNVERSIONED
                            && status.getContentsStatus() != (SVNStatusType.STATUS_NONE))
                        continue;
                    else if (f.isFile()) {
                        clientManager.getWCClient().doAdd(f, true, false, true, SVNDepth.fromRecurse(true), false, false, true);
                        targetList.add(f);
                        super.log("添加文件到提交隊列");
                    } else if (f.isDirectory()) {
                        // SVNDepth.empty 保證不遞歸文件夾下文件
                        clientManager.getWCClient().doAdd(f, false, false, false, SVNDepth.EMPTY, false, false, false);
                        targetList.add(f);
                        super.log("添加文件夾到提交隊列");
                    }
                }
            }
            long versionId = commit(targetList.toArray(new File[targetList.size()]), message, uLocks);
            if (versionId == -1)
                throw new Exception(ErrorVal.Commit_error);
            if (!isvnLog.addLog(this.svnAccount, SvnConfig.add, versionId, files))
                throw new Exception(ErrorVal.AddDbLog_error);
            return true;
        } catch (SVNException e) {
            super.log(e);
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public Long commit(File[] files, String message, boolean uLocks) {
        try {
            if (files.length == 0) {
                super.log("無效的提交信息");
                return -1l;
            }
            long versionId = clientManager.getCommitClient().doCommit(files, uLocks, message, null, null, false, false, SVNDepth.INFINITY).getNewRevision();
            super.log("提交隊列中預處理的操作操作  => 版本號: " + versionId);
            return versionId;
        } catch (SVNException e) {
            super.log(e);
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return -1l;
    }

    @Override
    public <T> boolean delete(String[] paths, boolean localDelete, String message, boolean uLock, ISvnDbLog<? extends T> isvnLog) {
        try {
            File[] files = checkFilePaths(paths);
            files = sortS_F(files);
            SVNStatus status = null;
            {
                List<File> targetList = new ArrayList<File>();
                List<List<File>> fileList = bindFile(files);
                for (int i = fileList.size() - 1; i >= 0; i--) {
                    for (File f : fileList.get(i)) {
                        if ((status = clientManager.getStatusClient().doStatus(f, true, true)) == null)
                            throw new Exception(ErrorVal.File_Repo_no_having);
                        else if (status.getContentsStatus() != SVNStatusType.STATUS_NORMAL)
                            throw new Exception(ErrorVal.Repo_Status_error + status.getContentsStatus().toString());
                        else {
                            clientManager.getWCClient().doDelete(f, false, localDelete, false);
                            if (f.isFile())
                                super.log("添加文件到刪除隊列");
                            else
                                super.log("添加文件夾到刪除隊列");
                            targetList.add(f);
                        }
                    }
                }
                long versionId = commit(targetList.toArray(new File[targetList.size()]), message, uLock);
                if (versionId == -1)
                    throw new Exception(ErrorVal.Commit_error);
                if (!isvnLog.addLog(this.svnAccount, SvnConfig.delete, versionId, files))
                    throw new Exception(ErrorVal.AddDbLog_error);
            }
            return true;
        } catch (SVNException e) {
            super.log(e);
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public <T> boolean update(String path, String message, boolean uLocks, ISvnDbLog<? super T> isvnLog) {
        try {
            File[] files = checkFilePaths(new String[] { path });
            // diffPath(files);
            long[] l = clientManager.getUpdateClient().doUpdate(files, SVNRevision.HEAD, SVNDepth.INFINITY, true, false);
            super.log("更新文件到操作隊列");
            long versionId = l[0];// commit(files, message, uLocks);
            if (versionId == -1)
                throw new Exception(ErrorVal.Update_no_change);
            if (!isvnLog.addLog(this.svnAccount, SvnConfig.update, versionId, files))
                throw new Exception(ErrorVal.AddDbLog_error);
        } catch (SVNException e) {
            super.log(e);
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public List<String> diffPath(File file) {
        try {
            if (file == null || !file.exists())
                throw new Exception(ErrorVal.Path_no_having);
            // 獲取SVNDiffClient
            SVNDiffClient diffClient = clientManager.getDiffClient();
            diffClient.setIgnoreExternals(false);
            StringOutputSteam os = new StringOutputSteam(new ArrayList<String>());
            diffClient.doDiff(new File[] { file }, SVNRevision.HEAD, SVNRevision.BASE, null, SVNDepth.INFINITY, true, os, null);
            super.log("比對庫路徑");
            return os.s;
        } catch (SVNException e) {
            super.log(e);
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    @Override
    public boolean cleanUp(File file) {
        try {
            if (!file.exists())
                throw new Exception(ErrorVal.Path_no_having);
            else if (!file.isDirectory())
                throw new Exception(ErrorVal.File_is_not_directory);
            clientManager.getWCClient().doCleanup(file, false, false, false, true, true, true);
            super.log("清理完成");
        } catch (SVNException e) {
            super.log(e);
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }

    @Override
    public boolean doLock() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean unLock() {
        // TODO Auto-generated method stub
        return false;
    }

}

實現代碼比較多。不要慌
仔細看看加斷點看看
加鎖解鎖還有一些接口的高級應用並沒有深入去寫,因爲畢竟是demo。上級覺得技術上是可以搞定這個功能的,雖然因爲其他原因項目閹割了 :( 所以也沒有後續進行深層開發
https://svnkit.com/documentation.html 這個是svnkit 文檔,不過文檔比svnkit.jar版本要老 :((


關於參數中

ISvnDbLog<? super T> isvnLog

可以讓使用者提供一個ISvnDbLog的實現,來進行日誌保存,無論是db還是file

最後我們提供一個簡單紡織小工廠

package com.svn.factory;

import com.svn.conf.ErrorVal;
import com.svn.conf.SvnConfig;
import com.svn.impl.SvnBaseImpl;
import com.svn.inf.ISvn;
import com.svn.model.SvnLinkPojo;

/**
 * DemoSvn主體
 * 
 * @author Allen
 * @date 2016年8月8日
 */
public final class DemoSvn {

    SvnLinkPojo svnLink;

    public SvnLinkPojo getSvnLink() {
        return svnLink;
    }

    /**
     * 私有構造
     */
    public DemoSvn() {
    }

    public DemoSvn(String svnAccount, String svnPassword, String repoPath) {
        this.svnLink = new SvnLinkPojo(repoPath, svnAccount, svnPassword);
    }

    /**
     * 獲取SVN操作
     * 
     * @param val
     *            default 不設置日誌狀態 log 開啓console日誌狀態
     * @throws 沒有操作匹配
     * @return {@link ISvn}
     */
    public ISvn execute(SvnConfig val) throws Exception {
        ISvn is = null;
        if (val == null)
            throw new Exception(ErrorVal.SvnConfig_is_null);
        switch (val.getVal()) {
        case "normal":
            is = new SvnBaseImpl(svnLink.getSvnAccount(), svnLink.getSvnPassword(), false, svnLink.getRepoPath());
            break;
        case "log":
            is = new SvnBaseImpl(svnLink.getSvnAccount(), svnLink.getSvnPassword(), true, svnLink.getRepoPath());
            break;
        default:
            throw new Exception(ErrorVal.SvnConfig_is_null);
        }
        return is;
    }
}

來一個測試函數吧
其實創建版本庫,操作對象,初始化實例,close都可以做的更簡潔,然後做到事務中,切面管理.. :)

package com.svn;

import java.io.File;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Date;

import com.svn.conf.SvnConfig;
import com.svn.factory.DemoSvn;
import com.svn.inf.ISvn;
import com.svn.inf.service.ISvnDbLog;
import com.svn.model.SvnRepoPojo;

/**
 * 測試類
 * 
 * @author Allen
 * @date 2016年8月8日
 */
public class exmple {
    String account = "allen";
    String password = "123456";
    String path = "http://192.168.1.xx/svn/abc";
    String targetHead = "e:/測試";
    ISvn svn;

    /**
     * 樣例
     * 
     * @throws Exception
     * @author Allen
     * @date 2016年8月12日
     */

    private void testCore() throws Exception {
        // 初始化實例
        DemoSvn ts = new DemoSvn(account, password, path);
        // 獲得操作對象
        this.svn = ts.execute(SvnConfig.log);
        // 得到版本庫信息
        svn.createSVNRepository();
        // 得到基礎操作對象
        svn.createSVNClientManager();
        /** 測試--Start-- **/
        testGetRepo();
        testCheckOut();
        testAdd();
        testDel();
        testCleanUp();
        testUpdate();
        testDiff();
        /** 測試 --End-- **/
        // 關閉庫容器
        svn.closeRepo();

    }

    /**
     * 獲得版本路徑文件信息
     * 
     * @author Allen
     * @date 2016年8月12日
     */
    private void testGetRepo() {
        print(svn.getRepoCatalog(""));
    }

    /**
     * 檢出到本地路徑
     * 
     * @author Allen
     * @date 2016年8月12日
     */

    private void testCheckOut() {
        svn.checkOut("xxx", targetHead);
    }

    /**
     * 添加文件到svn
     * 
     * @author Allen
     * @date 2016年8月12日
     */

    private void testAdd() {
        String[] strs = new String[] { targetHead + "/4/a/", targetHead + "/4/", targetHead + "/4/a/b/", targetHead + "/4/a/b/b.txt" };
        svn.add(strs, "haha", false, new ISvnDbLog<String>() {
            @Override
            public boolean addLog(String name, SvnConfig dbType, long versionId, File[] files) {
                System.out.println("Add 到 DB 了");
                return true;
            }

            @Override
            public List<String> getLog(String name, Date startTime, Date endTime) {
                System.out.println("get 到 log 了");
                return null;
            }
        });
    }

    /**
     * 刪除文件到svn
     * 
     * @author Allen
     * @date 2016年8月12日
     */

    private void testDel() {
        String[] strs = new String[] { targetHead + "/4/a/", targetHead + "/4/", targetHead + "/4/a/b/", targetHead + "/4/a/b/b.txt" };
        svn.delete(strs, true, "haha", false, new ISvnDbLog<String>() {
            @Override
            public boolean addLog(String name, SvnConfig dbType, long versionId, File[] files) {
                System.out.println("del 到 DB 了");
                return true;
            }

            @Override
            public List<String> getLog(String name, Date startTime, Date endTime) {
                System.out.println("get 到 log 了");
                return null;
            }
        });
    }

    /**
     * 更新文件到svn
     * 
     * @author Allen
     * @date 2016年8月12日
     */

    private void testUpdate() {
        String strs = targetHead + "/4/a/b/";
        svn.update(strs, "哈哈", false, new ISvnDbLog<String>() {
            @Override
            public boolean addLog(String name, SvnConfig dbType, long versionId, File[] files) {
                System.out.println("update 到 DB 了");
                return true;
            }

            @Override
            public List<String> getLog(String name, Date startTime, Date endTime) {
                System.out.println("get 到 log 了");
                return null;
            }
        });
    }

    /**
     * 測試庫比對
     * 
     * @author Allen
     * @date 2016年8月12日
     */

    private void testDiff() {
        String[] strs = new String[] { targetHead + "/4/a/b/" };
        List<String> s = svn.diffPath(new File(strs[0]));
        for (String t : s)
            System.out.println(t);
    }

    private void testCleanUp() {
        String[] strs = new String[] { targetHead + "/4/a/b/" };
        svn.cleanUp(new File(strs[0]));
    }

    /**
     * 打印當前版本庫路徑目錄
     */
    private void print(List<SvnRepoPojo> paramList) {
        System.out.print("commitMessage ");
        System.out.print("\t\t  date \t  ");
        System.out.print("\t   kind \t  ");
        System.out.print("\t name \t  ");
        System.out.print("\t repositoryRoot \t  ");
        System.out.print("\t revision \t  ");
        System.out.print("\t size \t  ");
        System.out.print("\t url \t  ");
        System.out.print("\t author \t  ");
        System.out.println("\t state \t  ");
        Collections.sort(paramList, new Comparator<SvnRepoPojo>() {
            @Override
            public int compare(SvnRepoPojo o1, SvnRepoPojo o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });
        for (SvnRepoPojo pojo : paramList) {
            System.out.print("\t" + pojo.getCommitMessage() + "\t");
            System.out.print("\t" + pojo.getDate().getTime() + "\t");
            System.out.print("\t" + pojo.getKind() + "\t");
            System.out.print("\t" + pojo.getName() + "\t");
            System.out.print("\t" + pojo.getRepositoryRoot() + "\t");
            System.out.print("\t" + pojo.getRevision() + "\t");
            System.out.print("\t" + pojo.getSize() + "\t");
            System.out.print("\t" + pojo.getUrl() + "\t");
            System.out.print("\t" + pojo.getAuthor() + "\t");
            System.out.print("\t" + pojo.getState() + "\t");
            System.out.print("\r\n");
        }
    }

    public static void main(String[] args) throws Exception {
         new exmple().testCore(); 
    }
}

就這樣吧。demo而已看個用法和思路。具體優化及細節也沒有時間去做了,畢竟是demo :)

下載地址
http://download.csdn.net/detail/crazyzxljing0621/9755958

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章