文章目录
SVN自动生成升级文件夹产出物整理
因为每天都要往现场发两三次包,一个一个的挪文件实在费事,因此写了这个工具来减少无所谓的时间浪费。
自从开始写,到现在,大致经历了四个版本,现在总结的是V4.0,后续可能还会有新的变化,到时候再更新一下
文件路径帮助类FilePathUtil
这个类主要是用来统一Linux和Windows下文件路径的问题
这个地方其实还是没有处理的让自己满意,因为在核心类SVNMain中还是多次出现了对路径的特殊处理,后边可能还是要再优化
package com.svn.util;
/**
* 文件路径切换。主要是为了兼容windows和Linux两种系统
*
* @author lihh
*
*/
public class FilePathUtil {
public static final String FILE_SEPARATOR = System.getProperty("file.separator");
// public static final String FILE_SEPARATOR = File.separator;
public static String getRealFilePath(String path) {
return path.replace("/", FILE_SEPARATOR).replace("\\", FILE_SEPARATOR);
}
public static String getHttpURLPath(String path) {
return path.replace("\\", "/");
}
/**
* 判断系统类型
*
* @return
*/
public static boolean isWinOs() {
return System.getProperty("os.name").toLowerCase().startsWith("win");
}
}
总结
这个类让我学习到的是
String.format
这个平常用的少了,都是直接用+来处理。这里没有用到很高级的东西,主要连接字符串的另外一种形式
操作系统类型的判断
System.getProperty("os.name").toLowerCase()
作系统文件路径分割符号的获取
System.getProperty("file.separator")
或者
File.separator
命令执行类CmdUtil
该类用来在Linux或者Windows下执行命令
package com.svn.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* 执行命令帮助类
*
* @author lihh
*
*/
public class CmdUtil {
/**
* 组装命令字符串
* @return
*/
private static String getCmdStr() {
boolean isWinOs = FilePathUtil.isWinOs();
String panfu = PropertyUtil.getProjectPath().substring(0, 2);
return String.format("%s cd %s&&svn diff -r %s --summarize",
isWinOs ? "cmd /c " + panfu +"&&" : "",
FilePathUtil.getRealFilePath(PropertyUtil.getProjectPath()),
PropertyUtil.getVersion());
}
/**
* 执行命令
*
* @param cmd
* @return
*/
public static String[] runCmd() {
Runtime runtime = Runtime.getRuntime();
Process process = null;
BufferedReader reader = null;
StringBuffer content = new StringBuffer();
try {
String cmd = getCmdStr();
if (FilePathUtil.isWinOs()) {
System.out.println(" 开始执行Windows命令:" + cmd);
process = runtime.exec(cmd);
} else {
System.out.println(" 开始执行Linux命令:" + cmd);
String[] cmdA = { "/bin/sh", "-c", cmd };
process = runtime.exec(cmdA);
}
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();// 等待子进程完成再往下执行。
String line = null;
while ((line = reader.readLine()) != null) {
content.append(line + "\r\n");
}
//如果命令执行错误,那么输出错误信息
if(content.toString().equals("")) {
reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
while ((line = reader.readLine()) != null) {
content.append(line + "\r\n");
}
}
} catch (IOException e1) {
e1.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
reader = null;
}
int i = process.exitValue();// 接收执行完毕的返回值
process.destroy();// 销毁子进程
process = null;
return new String[]{String.valueOf(i),content.toString()};
}
}
总结
这个类中的内容之前的工作学习中用到的都比较少,很多都是参考网络上的资料来写的。
Runtime和Process可以查阅API
Runtime
Runtime代表着Java运行时的环境,每个Java程序运行时都会自动创建一个Runtime,我们可以通过Runtime.getRuntime来获取Java运行时环境
Runtime的大致用处有
- 统计系统的内存、cpu等信息
- 运行命令。在Windows中,相当于在cmd中运行;在Linux中则相当于在终端中运行。
Process
通过Runtime.getRuntime().exec(“命令”)可以获得一个进程,这个进程用Process来表示,Process实例可以用来控制进程并获得相关信息。
需要注意的是,在Windows和Linux下命令的执行方式是不同的,需要自己去处理这些细节
####获取命令的输出
Process的实例中包含了执行成功和失败后的输出
process.getInputStream//获取程序执行成功后的输出
process.getErrorStream//获取程序执行错误后的输出
process.exitValue()//获取程序运行的返回值,0执行成功,其它执行失败
process.destory()//销毁子进程
##文件操作类
这个比较简单了。主要是文件及文件夹的创建
package com.svn.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
public class FileUtil {
/***
* 复制单个文件*
*
* @param oldPath
* String 原文件路径 如:c:/fqf.txt*
* @param newPath
* String 复制后路径 如:f:/fqf.txt*@return boolean
*/
private static void mkDir(File file) {
if (file.getParentFile().exists()) {
file.mkdir();
} else {
mkDir(file.getParentFile());
file.mkdir();
}
}
public static void copyFile(String oldPath, String newPath) {
try {
oldPath = FilePathUtil.getRealFilePath(oldPath);
newPath = FilePathUtil.getRealFilePath(newPath);
String[] arr1 = newPath.split("\\.");
//注意这里:为多操作系统做判断.windows是\\
String[] arr2 = arr1[0].split(FilePathUtil.isWinOs() ? File.separator+File.separator : File.separator);
// String fileName = arr2[arr2.length - 1] + "." + arr1[1];
// System.out.println(fileName);
String filePath = "";
for (int i = 0; i <= arr2.length - 2; i++) {
filePath += arr2[i] + File.separator;
}
// System.out.println(filePath);
File file = new File(filePath);
mkDir(file);
// int bytesum = 0;
int byteread = 0;
File oldfile = new File(oldPath);
if (oldfile.exists()) { // 文件存在时
InputStream inStream = new FileInputStream(oldPath); // 读入原文件
@SuppressWarnings("resource")
FileOutputStream fs = new FileOutputStream(newPath);
byte[] buffer = new byte[1444];
// int length;
while ((byteread = inStream.read(buffer)) != -1) {
// bytesum += byteread; // 字节数 文件大小
// System.out.println(bytesum);
fs.write(buffer, 0, byteread);
}
inStream.close();
}
} catch (Exception e) {
System.out.println("复制单个文件操作出错");
e.printStackTrace();
}
}
}
properties文件的读取
同样比较简单
package com.svn.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Properties;
/**
* @Desc:properties文件获取工具类 Created by lihhz on 2017/7/4.
*/
public class PropertyUtil {
private static InputStream in;
private static Properties props;
/**
* 加载conf.properties
*/
synchronized private static void loadProps() {
props = new Properties();
try {
in = PropertyUtil.class.getClassLoader().getResourceAsStream("conf.properties");
props.load(new InputStreamReader(in, "utf-8"));
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != in) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 获取conf属性值
*
* @param key
* @return
*/
public static String getProperty(String key) {
if (null == props) {
loadProps();
}
return props.getProperty(key);
}
public static String getProjectPath() {
return getProperty("proectPath");
}
public static String getTargetPath() {
return getProperty("targetPath");
}
public static String getVersion() {
return getProperty("version");
}
}
使用jdk解析xml
这里解析的是eclipse生成的.classpath文件,这个文件中包含着classes的来源及输出信息,用来控制svn生成文件的去向
package com.svn.util;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* 解析.classpath文件
*
* @author lihh
*
*/
public class ResolveClasspath {
/**
* 解析出classpath中与项目有关的路径和输出路径
* @return
*/
public static List<Map<String, String>> getInfo() {
Element root = null;
DocumentBuilder docBuilder = null;
DocumentBuilderFactory docBuilderFactory = null;
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
try {
File classpath = getClasspath();
docBuilderFactory = DocumentBuilderFactory.newInstance();
docBuilder = docBuilderFactory.newDocumentBuilder();
Document doc = docBuilder.parse(classpath);
root = doc.getDocumentElement();
NodeList nodeList = root.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node classpathentryNode = nodeList.item(i);
if (classpathentryNode.getNodeName().equals("classpathentry")) {
// 避免#text
NamedNodeMap map = classpathentryNode.getAttributes();
if (map != null) {
Map<String, String> m = new HashMap<String, String>();
Node item = map.getNamedItem("path");
if (item.getNodeValue().startsWith("src")) {
m.put(item.getNodeName(), item.getNodeValue());
Node output = map.getNamedItem("output");
m.put(output.getNodeName(), output.getNodeValue());
list.add(m);
}
}
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return list;
}
public static void main(String[] args) {
List<Map<String, String>> list = getInfo();
for (Map<String, String> map : list) {
System.out.println(map);
}
}
/**
* 找.classpath文件
* @return
* @throws FileNotFoundException
*/
private static File getClasspath() throws FileNotFoundException {
String proRootPath = FilePathUtil.getRealFilePath(PropertyUtil.getProjectPath());
File proDir = new File(proRootPath);
if (!proDir.isDirectory()) {
// 不是文件夹,来个报错
return null;
}
String classpathPath = FilePathUtil.getRealFilePath(proDir.getAbsolutePath() + "/.classpath");
File classpath = new File(classpathPath);
if (!classpath.exists()) {
throw new FileNotFoundException("没有找到.classpath文件,程序退出");
}
return classpath;
}
}
核心类
这个类要做优化,因为还有很多对路径的特殊处理。优化的思路是:将所有的路径都转为Linux中的正斜杠路径表示法,然后统一处理。
package com.svn.util;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SvnMain {
/**
* 核心方法
* @param content
*/
private static void packFiles(String content) {
// 原始
final String PROJECT_PATH = PropertyUtil.getProjectPath(), TARGET_PAHT = PropertyUtil.getTargetPath();
System.out.println("-----开始组装文件-----");
String[] contents = content.split("\r\n");
for (int i = 0; i < contents.length; i++) {
String line = contents[i];
System.out.println(line);
if (line == null || line.equals("")) {
continue;
}
//注意这里:为多操作系统做判断.windows是\\
String regex = FilePathUtil.isWinOs() ? "src\\\\([\\\\a-zA-Z0-9_\\-\\.]+)" : "src/([/a-zA-Z0-9_\\-\\.]+)";
Matcher matcher = Pattern.compile(regex).matcher(line);
while (matcher.find()) {
String relativePath = matcher.group(0);
if (relativePath.contains(".") && !relativePath.equals(".")) {//只处理文件
System.out.println(String.format("%s.开始处理文件:%s", i, relativePath));
String oldPath = PROJECT_PATH + FilePathUtil.FILE_SEPARATOR + relativePath;
List<Map<String, String>> classpathList = ResolveClasspath.getInfo();
boolean isInClasspath = false;
for (Map<String, String> map : classpathList) {
final String path = map.get("path"), output = map.get("output");
String temp = FilePathUtil.isWinOs() ? relativePath.replace("\\", "/") : relativePath;
if (temp.startsWith(path)) {
isInClasspath = true;
if(FilePathUtil.isWinOs()){
relativePath = relativePath.replace(path.replace("/","\\"),"");
}else{
relativePath = relativePath.replace(path,"");
}
final String from = PROJECT_PATH + FilePathUtil.FILE_SEPARATOR + output + relativePath;
System.out.println(" 将从路径:"+from+"拷贝文件!");
if(oldPath.endsWith(".java")) {
FileUtil.copyFile(from.split("\\.")[0] + ".class",
TARGET_PAHT+"/WEB-INF/classes/" + relativePath.split("\\.")[0] + ".class");
// 对内部类的支持
//TODO:测试这个/在Linux和Windows下是否相同
File parentDir = new File(from.split("\\.")[0] + ".class").getParentFile();
File[] fileList = parentDir.listFiles();
if(fileList != null) {
for (File file : fileList) {
String fileName = file.getName();
String className = relativePath.substring(relativePath.lastIndexOf(FilePathUtil.isWinOs()?"\\":"/")+1).split("\\.")[0];
if(fileName.startsWith(className)) {
System.out.println();
}
if(fileName.startsWith(className+"$")) {
FileUtil.copyFile(file.getAbsolutePath(),
TARGET_PAHT+"/WEB-INF/classes/" + relativePath.split("\\.")[0].replace(className,fileName));
}
}
}
}else {//处理web文件
FileUtil.copyFile(from,TARGET_PAHT+"/WEB-INF/classes" + relativePath);
}
} else {
continue;
}
}
if(!isInClasspath) {
//不是class,是webapp。照搬即可
String temp = FilePathUtil.isWinOs()?"src\\main\\webapp\\":"src/main/webapp/";
FileUtil.copyFile(oldPath, TARGET_PAHT + File.separator + relativePath.replace(temp, ""));
}
} else {
//TODO:这里处理的是文件夹,先不管。因为空文件夹不必处理
System.out.println(" 文件夹暂不处理");
}
}
}
System.out.println("-----组装文件结束-----");
}
public static void main(String[] args) throws InterruptedException {
String[] ret = CmdUtil.runCmd();
// 0:执行完成;否则执行失败
if (ret[0].equals("0")) {
packFiles(ret[1]);
} else {
System.out.println("命令执行失败!失败原因:" + ret[1]);
System.exit(0);
}
}
}
配置文件
主要是用来存储项目的来源,输出路径以及版本信息。
###注意路径统一使用正斜杠表示
#################################################################
# Windows下路径表示方法 #
#################################################################
##项目路径(包含项目名)
proectPath=E:/data/workspace/cpams
##js css 等
targetPath=C:/Users/Administrator/Desktop/cpams
#开始版本:结束版本
version=37240:37244
#################################################################
#################################################################
# Linux下路径表示方法 #
#################################################################
##项目路径(包含项目名)
#proectPath=/media/lihh/新加卷/data/workspace/cpams
##js css 等
#targetPath=/home/lihh/Desktop/cpams
#开始版本:结束版本
#version=37240:37244
#################################################################
#################################################################
# 弃用的配置 #
#################################################################
##要复制的classes文件夹的路径
#classPath=/media/lihh/新加卷/data/workspace/cpams/target/classes
##svn diff -r 37047:37051 --summarize
##svn st
#cmd=cd /media/lihh/新加卷/data/workspace/cpams&&svn st
##classes conf中的properties
#disRootPath=/home/lihh/Desktop/cpams/WEB-INF/classes
2018/4/25更新
使用svnkit来代替svn.exe,需要引入svnkit的jar
Scanner的hasNext恒为false
这里遇到一个问题就是Scanner,尽管是在不同的类中new Scanner和close,但是依然影响了Scanner的使用,目前看来,只能在最后一个scanner后关闭,否则靠后的class中的hasNext会一直是false。
package com.svn.util;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.SVNLogEntryPath;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.BasicAuthenticationManager;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc2.ISvnObjectReceiver;
import org.tmatesoft.svn.core.wc2.SvnLog;
import org.tmatesoft.svn.core.wc2.SvnOperationFactory;
import org.tmatesoft.svn.core.wc2.SvnRevisionRange;
import org.tmatesoft.svn.core.wc2.SvnTarget;
/**
* 使用svnkit将对比内容导出为string
* @author lihhz
*
*/
public class SvnUtil {
/**
* 使用svnkit将对比内容导出为string
* @return
* @throws SVNException
* @throws IOException
* @throws Exception
*/
@SuppressWarnings("deprecation")
public static String getChangeStr() throws SVNException, IOException{
@SuppressWarnings("resource")
Scanner scanner = new Scanner(System.in);
int index = 1;
//用户信息
String account = PropertyUtil.getUserAccount(),pass=PropertyUtil.getPassword();
if(account == null || account.equals("")){
System.out.println("没有找到用户信息!");
System.out.println(index++ + ".请输入用户名");
if(scanner.hasNext()){
account=scanner.next();
PropertyUtil.WriteProperties("userAccount", account);
}
System.out.println(index++ + ".请输入密码:");
if(scanner.hasNext()){
pass=scanner.next();
PropertyUtil.WriteProperties("password", pass);
}
}
//版本信息
Long bVersion ,eVersion;
while(true){
System.out.println(index++ + ".请输入起始版本号(导出时,不包括该版本代码):");
while(true){
if(scanner.hasNextLong()){
bVersion=scanner.nextLong();
break;
}else{
System.out.println("起始版本号无效,请重新输入!");
}
}
System.out.println(index++ + ".请输入结束版本号:");
while(true){
if(scanner.hasNextLong()){
eVersion=scanner.nextLong();
break;
}else{
System.out.println("结束版本号无效,请重新输入!");
}
}
if(bVersion <= eVersion){
break;
}else{
System.out.println("错误:结束版本号大于起始版本号");
}
}
//这里不能有close,否则后边的Scanner就用不了了
// scanner.close();
final StringBuffer sb = new StringBuffer();
// 实例化客户端管理类
final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
svnOperationFactory.setAuthenticationManager(new BasicAuthenticationManager(account,pass));// svn用户名密码
final SvnLog log = svnOperationFactory.createLog();
log.addRange(SvnRevisionRange.create(SVNRevision.create(bVersion), SVNRevision.create(eVersion)));
log.setDiscoverChangedPaths(true);
log.setSingleTarget(SvnTarget.fromURL(SVNURL.parseURIEncoded(PropertyUtil.getSvnUri())));
System.out.println("---------------修改内容---------------");
log.setReceiver(new ISvnObjectReceiver<SVNLogEntry>() {
public void receive(SvnTarget svnTarget, SVNLogEntry svnLogEntry) throws SVNException {
// 每个版本执行一次
// System.out.println("版本:" + arg1.getRevision() + "===========作者:" + arg1.getAuthor() + "======时间:" + sdf2.format(arg1.getDate()));
System.out.println(svnLogEntry.getMessage());
Map<String, SVNLogEntryPath> map = svnLogEntry.getChangedPaths();
if (map.size() > 0) {
Set<String> set = map.keySet();
for (Iterator<String> iterator = set.iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
sb.append(key.replace("/source/project/trunk/BJ/注册会计师行业监管系统/cpams/cpams/", "")).append("\r\n");
// SVNLogEntryPath path = map.get(key);
// System.out.println(typeDic.get(path.getType() + "") + ":" + key);
// handleFile(key);
}
}
}
});
log.run();
System.out.println("----------------------------------");
return sb.toString();
}
}
Console
java提供System.console()方法获取Console,需要注意的是这个Console在开发工具中为null,只有在cmd中才是非空的。
使用readPassword**方法可以避免密码显示在cmd上。
//使用console
//好处是:避免控制台显示密码
//缺点是:无法再eclipse控制台调试
Console console = System.console();
if (console == null) {
throw new IllegalStateException("Console is not available!");
}
if(account == null || account.equals("")){
System.out.println("没有找到用户信息!");
account = console.readLine(CommonUtil.COUNTER++ + ".请输入用户名:");
char[] password = console.readPassword(CommonUtil.COUNTER++ + ".请输入密码:");
pass = String.valueOf(password);
}
2018/4/25更新结束