`
xinklabi
  • 浏览: 1564262 次
  • 性别: Icon_minigender_1
  • 来自: 吉林
文章分类
社区版块
存档分类
最新评论
收藏列表
标题 标签 来源
LinuxUtils类 java linux
//LinuxUtils
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.SCPClient;

import com.alipay.aqc.common.exception.BusinessException;
import com.alipay.aqc.common.exception.UnknownException;
import com.alipay.aqc.common.util.LinuxUtils.ITransferFileListener;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;

/**
 * 此类相比LinuxUtils类更新,主要是因为LinuxUtils类中读取命令执行结果的方法失效,所以新写一个类
 * 如果有需要用到原来类的方法,但是读取不到结果,请在此类中使用新的api实现,把原来的方法标记为过时
 * 
 */
public class LinuxUtils2 {
    private static Log logger = LogFactory.getLog(LinuxUtils2.class);

    public static void main(String[] args) throws Exception {
        String hostname = "tradecore-1-64.test.alipay.net";// 要登陆目标主机
        String username = "admin";// 登陆用的用户名
        String password = "jiaoyi123"; // 登陆用到的密码

        /* Create a connection instance */
        LinuxSession conn = getSession(hostname, username, password);
        //
        // //取远程文件
        // File file = getRemoteFile(conn, "/home/admin/deploy.sh",
        // "C:/一般文件/",null);
        // if(file.exists()){
        // System.out.println(CharsetUtils.guessCharset(file));
        // }

        List<String> findByFileNames = findByFileNames(conn,
            "/home/admin/tradecore/target/tradecore.ace/core", new String[] { "*.jar" });
        System.out.println(findByFileNames);

        getRemoteDir(hostname, username, password,
            "/home/admin/tradecore/target/tradecore.ace/core", "*.jar",
            "/Users/xinkeliang/Downloads/jars");

        close(conn);

    }

    public static interface CommandCallBack<T> {

        void handle(T br) throws Exception;

    }

    static public class ResultToListCommandCallBack implements CommandCallBack<LinuxSession> {
        private final List<String> resultLines;

        public ResultToListCommandCallBack(List<String> resultLines) {
            this.resultLines = resultLines;
        }

        @Override
        public void handle(LinuxSession sess) {
            ExecuteShellResult result = sess.result;
            if (result.isHasException) {
                resultLines.add(result.errorResult);
            } else {
                String resultString = result.result;
                if (resultString != null) {
                    String[] lines = resultString.split("\\n");
                    resultLines.addAll(Arrays.asList(lines));
                }
            }
        }
    }

    //	private static List<String> findByFileNames(String host, String userName,
    //			String password, String remoteDir, String[] fileNames) {
    //		LinuxSession session = getConnection(host, userName, password);
    //		List<String> files = findByFileNames(session, remoteDir, fileNames);
    //		close(session);
    //		return files;
    //	}

    private static List<String> findByFileNames(LinuxSession session, String remoteDir,
                                                String[] fileNames) {
        if (fileNames == null) {
            return null;
        }
        final List<String> files = new ArrayList<String>();
        for (int i = 0; i < fileNames.length; i++) {
            String command = "find " + remoteDir + " -name "
                             + getValidFileNamePattern(fileNames[i]);
            execCommand(session, command, new ResultToListCommandCallBack(files));
        }
        return files;
    }

    /**
     * 如果没有用引号括起来的,加上
     * @param fileNamePattern
     * @return
     */
    static String getValidFileNamePattern(String fileNamePattern) {
        if (fileNamePattern == null) {
            return null;
        }
        StringBuilder sb = new StringBuilder(fileNamePattern);
        if (!fileNamePattern.startsWith("\"") && !fileNamePattern.startsWith("'")) {
            sb.insert(0, "\"");
        }
        if (!fileNamePattern.endsWith("\"") && !fileNamePattern.endsWith("'")) {
            sb.append("\"");
        }

        return sb.toString();
    }
    
    /**
     * 执行远程命令
     * @param host
     * @param userName
     * @param password
     * @param command
     * @param commandEncoding 发送命令时使用的编码:utf-8 , gbk ....,结果读取时默认用gbk
     */
    public static List<String> executeCommand(String host, String userName,
			String password, String command, String commandEncoding, String resultEncoding) {
		LinuxSession session = getSession(host, userName, password);
		List<String> output = new ArrayList<String>();
		execCommand(session, command, commandEncoding, resultEncoding, new LinuxUtils2.ResultToListCommandCallBack(output));
		close(session);
		return output; 
	}

    /**
     * 执行远程命令
     * @param host
     * @param userName
     * @param password
     * @param command
     * @param callBack 可以为null
     */
    public static void execCommand(String host, String userName,
			String password, String command,
			CommandCallBack<LinuxSession> callBack) {
		LinuxSession session = getSession(host, userName, password);
		execCommand(session, command, "UTF-8", "GBK", callBack);
		close(session);
	}
	
	public static void execCommand(String host, String userName,
			String password, String command, String commandEncoding,
			CommandCallBack<LinuxSession> callBack) {
		LinuxSession session = getSession(host, userName, password);
		execCommand(session, command, commandEncoding, "GBK", callBack);
		close(session);
	}

	static byte[] str2byte(String str, String encoding){
	    if(str==null) 
	      return null;
	    try{ return str.getBytes(encoding); }
	    catch(java.io.UnsupportedEncodingException e){
	      return str.getBytes();
	    }
	  }
	
	private static void execCommand(LinuxSession session, String command,
			CommandCallBack<LinuxSession> callBack) {
		//
		execCommand(session, command, "UTF-8", "GBK", callBack);
	}
	

	private static void execCommand(LinuxSession session, String command, String commandEncoding, String resultEncoding,
			CommandCallBack<LinuxSession> callBack) {
		ExceptionUtils.throwIfEmpty(command, "错误:命令为空");

		ChannelExec channelExec = null;
		try {
			ExecuteShellResult result = new ExecuteShellResult();
			session.setResult(result);

			channelExec = session.openChannel();

			//channelExec.setCommand(command);
			channelExec.setCommand(str2byte(command, commandEncoding));

			ByteArrayOutputStream os = new ByteArrayOutputStream();
			channelExec.setOutputStream(os);

			ByteArrayOutputStream eos = new ByteArrayOutputStream();
			channelExec.setErrStream(eos);

			channelExec.connect();

			if (logger.isDebugEnabled()) {
				logger.debug("远程调用Shell 脚本:" + command);
			}

			// 等 0.1 秒.
			TimeUnit.MILLISECONDS.sleep(100);

			while (!channelExec.isClosed()) {
				TimeUnit.SECONDS.sleep(5);
			}

			result.exitValue = channelExec.getExitStatus();

			if (os != null && os.size() > 0) {
				result.result = os.toString(resultEncoding);
			}

			if (eos != null && eos.size() > 0) {
				result.errorResult = eos.toString();
			}

			if (callBack != null) {
				callBack.handle(session);
			}

			os.close();
			eos.close();

		} catch (Exception e) {
			if (isNoPermissionError(e)) {
				throw new BusinessException("用户权限不够,请更换用户");
			}
			throw new UnknownException(e);
		} finally {
			if (channelExec != null)
				channelExec.disconnect();
		}
	}

    /**
     * 取得链接
     * 
     * @param host
     * @param userName
     * @param password
     * @return
     */
    private static LinuxSession getSession(String host, String userName, String password) {
        ExceptionUtils.throwIfEmpty(host, "建立远程连接出错,远程地址不能为空");
        return new LinuxSession(host, userName, password);
    }

    /**
     * @author only openSession(),close can be used
     */
    static public class LinuxSession {

        com.jcraft.jsch.Session sshSession;
        String                  host;
        String                  userName;
        String                  password;
        int                     port = 22;
        ExecuteShellResult      result;

        public LinuxSession(String host, String userName, String password) {
            this.host = host;
            this.userName = userName;
            this.password = password;
            createConnection();
        }

        public void setResult(ExecuteShellResult result) {
            this.result = result;
        }

        public ChannelExec openChannel() {
            return openChannel("exec");
        }

        public ChannelExec openChannel(String type) {
            try {
                return (ChannelExec) sshSession.openChannel(type);
            } catch (JSchException e) {
                throw new UnknownException("打开通道出错", e);
            }
        }

        private void createConnection() {
            try {

                JSch jsch = new JSch();
                sshSession = jsch.getSession(userName, host, port);

                if (password != null) {
                    sshSession.setPassword(password);
                } else {
                    String privateKey = "/home/" + userName + "/.ssh/id_rsa";
                    jsch.addIdentity(privateKey);
                }

                Properties sshConfig = new Properties();

                // "StrictHostKeyChecking"如果设为"yes",
                // ssh将不会自动把计算机的密匙加入"$HOME/.ssh/known_hosts"文件,
                // 且一旦计算机的密匙发生了变化,就拒绝连接。
                sshConfig.put("StrictHostKeyChecking", "no");
                sshSession.setConfig(sshConfig);

                sshSession.connect();

                synchronized (sessions) {
                    sessions.put(this, System.currentTimeMillis());
                }
            } catch (Exception e) {
                throw new BusinessException("建立远程连接出错:" + e.getMessage(), e);
            }
        }

        public synchronized void close() {
            sshSession.disconnect();
        }

    }

    private static Map<LinuxSession, Long> sessions = new HashMap<LinuxSession, Long>();

    private static class ConnectionMoniter implements Runnable {
        private static final long               MAX_ALIVE_TIME  = 90 * 1000;                    // 最大存活时间90秒种
        private static final List<LinuxSession> deadConnections = new ArrayList<LinuxSession>();

        public void run() {
            while (true) {
                try {
                    synchronized (sessions) {
                        if (!sessions.isEmpty()) {
                            for (Iterator<LinuxSession> iterator = sessions.keySet().iterator(); iterator
                                .hasNext();) {
                                LinuxSession conn = iterator.next();
                                if ((System.currentTimeMillis() - sessions.get(conn)) >= MAX_ALIVE_TIME) {
                                    deadConnections.add(conn);
                                }
                            }
                        }
                    }
                    if (deadConnections.size() > 0) {
                        for (Iterator<LinuxSession> iterator = deadConnections.iterator(); iterator
                            .hasNext();) {
                            LinuxUtils2.close(iterator.next());
                            iterator.remove();
                        }
                    }
                } catch (Throwable e) {
                    // StringBuilder content = new StringBuilder();
                    // MailUtils.appendStackTrace(content, e);
                    // MailUtils.sendMessage("异常信息邮件:远程连接监视器发生错误",content.toString());
                }
                try {
                    Thread.sleep(5000);
                } catch (Throwable e) {
                }
            }
        }
    }

    static {// 全程监视联接情况
        new Thread(new ConnectionMoniter()).start();
    }

    private static void close(Object connectionOrSession) {
        if (connectionOrSession == null) {
            return;
        }
        synchronized (sessions) {
            sessions.remove(connectionOrSession);

            if (connectionOrSession instanceof LinuxSession) {
                ((LinuxSession) connectionOrSession).close();
            } else {
                throw new UnknownException("未知的对象类型,无法关闭");
            }
        }
    }

    /**
     * Shell 脚本执行结果.<br>
     * 可以获取: <li>进程出口值. {@link ExecuteShellResult#getExitValue()} <br> <li>
     * 进程输出流输出的结果. {@link ExecuteShellResult#getResult()} <br> <li>进程错误输出流输出的结果.
     * {@link ExecuteShellResult#getErrorResult()} <br>
     */
    public static class ExecuteShellResult {

        /**
         * 执行过程中没有异常.
         */
        boolean isHasException;

        /**
         * 如果发生异常, cause 不能为空.
         */
        //		private Exception cause;

        //		/**
        //		 * 进程的出口值。根据惯例,0 表示正常终止。
        //		 */
        int     exitValue;

        /**
         * 进程输出流输出的结果.
         */
        String  result      = "";

        /**
         * 进程错误输出流输出的结果.
         */
        String  errorResult = "";

    }

    public static void getRemoteDir(String host, String userName, String password,
                                    String remoteDir, String fileName, String localDir) {
        LinuxSession session = getSession(host, userName, password);
        List<String> files = findByFileNames(session, remoteDir, new String[] { fileName });
        close(session);

        Connection connection = LinuxUtils.getConnection(host, userName, password);
        if (!files.get(0).isEmpty()) {
            getRemoteDir(connection, remoteDir, files, localDir, null);
        }
        LinuxUtils.close(connection);
    }

    private static File getRemoteDir(Connection connection, String remoteDir,
                                     final List<String> files, String localDir,
                                     ITransferFileListener listener) {
        ExceptionUtils.throwIfEmpty(localDir, "错误:本地目录文件名为空");

        File dir = new File(localDir);

        if (files.size() == 0) {
            return dir;//没有下载到有文件
        }
        //同名类,要先放起来,逐个取,因为取回来是放在同一目录的,所以不能一起取
        List<String> tongMingLeis = new ArrayList<String>(5);
        //装所有类名,用来判断是否重复
        List<String> tempList = new ArrayList<String>(files.size());

        //如果有内部类,把类名加入“\\”,否则取时会出错
        for (int i = 0; i < files.size(); i++) {
            String clazz = files.get(i);
            if (clazz.contains("$")) {
                files.set(i, clazz.replace("$", "\\$"));
            }
            String clazzName = clazz.replace('\\', '/');
            //文件名
            if (clazzName.contains("/")) {
                clazzName = clazzName.substring(clazzName.lastIndexOf('/'));
            }

            if (tempList.contains(clazzName)) {
                //加入同名类中
                tongMingLeis.add(clazz);
                //从批量取的集合中去除,后面另外单独取
                files.remove(i);
                i--;
            }
            //装到集合中
            tempList.add(clazzName);
        }
        //用完了,释放集合
        tempList = null;

        //装到数组中
        String[] array = files.toArray(new String[files.size()]);

        //创建本地文件夹
        if (!dir.exists()) {
            dir.mkdirs();
        }

        //创建客户端
        SCPClient client = new SCPClient(connection);
        try {
            //分批取文件,每批100个
            int maxCount = 100;
            String[] dest = new String[100];
            for (int i = 0; (i * maxCount) < array.length; i++) {
                if (array.length < ((i + 1) * maxCount)) {//改成分批进行,太多了SCP时会出错
                    dest = new String[array.length - i * maxCount];
                }
                System.arraycopy(array, i * maxCount, dest, 0, dest.length);

                if (listener != null) {
                    listener.transferFileMsg("正在获取文件:" + dest[0]);
                }
                //取文件
                client.get(dest, localDir);
            }

            //把文件从“dir”这个目录放回到原来的目录结构中
            moveToOrigalPath(files, remoteDir, localDir);

            //取同名类回来,同名类中不论是否多次同名,每次取一个,主要是为了编程简单,
            for (String file : tongMingLeis) {
                try {
                    //取回单个文件,放在当前目录中
                    getRemoteFile(connection, file, localDir, listener);
                } catch (Exception e) {
                    if (isNoPermissionError(e)) {
                        throw new BusinessException("用户权限不够,请更换用户");
                    } else if (isNotARegularFile(e)) {//可能是一个文件夹,取不回来,那么直接在本址建文件夹好了
                        String filePath = file.replace(remoteDir, "");
                        filePath = filePath.replace("\\$", "$");
                        //以localDir为基准,恢复原来的层次结构
                        File toFile = new File(localDir + filePath);
                        toFile.mkdirs();
                        continue;
                    } else {
                        throw new UnknownException(e);
                    }
                }
                //把文件从“dir”这个目录放回到原来的目录结构中
                moveToOrigalPath(Arrays.asList(new String[] { file }), remoteDir, localDir);
            }

            return dir;
        } catch (IOException e) {
            if (isNoPermissionError(e)) {
                throw new BusinessException("用户权限不够,请更换用户");
            }
            throw new UnknownException(e);
        }
    }

    /**
     * 提示不规则文件错
     * @param e
     * @return
     */
    static boolean isNotARegularFile(Throwable e) {
        while (e != null) {
            if (e.getMessage() != null && e.getMessage().contains("not a regular file")) {
                return true;
            }
            e = e.getCause();
        }
        return false;
    }

    /**
     * 把文件搬回到原来的位置
     * 1、文件已全都放在localDir中,没有层次
     * 2、把remoteFiles中的字符串删除掉remoteDir字符串,即得到文件本来的目录层次
     * 3、将这个层次前面加上localDir中,即保留了原来的目录层次
     * @param remoteFiles
     * @param remoteDir
     * @param localDir
     */
    static void moveToOrigalPath(List<String> remoteFiles, String remoteDir, String localDir) {
        if (remoteFiles.size() == 0) {
            return;
        }
        for (String filePath : remoteFiles) {
            //把remoteFiles中的字符串删除掉remoteDir字符串,即得到文件本来的目录层次
            filePath = filePath.replace(remoteDir, "");
            filePath = filePath.replace("\\$", "$");
            filePath = filePath.replace('\\', '/');

            //文件名
            String fileName = filePath.substring(filePath.lastIndexOf('/'));

            //文件已全都放在localDir中,没有层次,所以下面的代码可以得到当前文件绝对路径
            File fromFile = new File(localDir + fileName);
            //以localDir为基准,恢复原来的层次结构
            File toFile = new File(localDir + filePath);
            //建父文件夹
            File parentFile = toFile.getParentFile();
            if (!parentFile.exists()) {
                parentFile.mkdirs();
            }
            try {
                //移动文件
                FileUtils.copyFile(fromFile, toFile);
                com.alipay.aqc.common.util.FileUtils.deleteFile(fromFile);
            } catch (IOException e) {
            }
        }
    }

    static boolean isNoPermissionError(Exception e) {
        if (e == null) {
            return false;
        }
        if (e.getMessage() == null) {
            return false;
        }
        if (e.getMessage().contains("Permission denied")) {
            return true;
        }
        if (e.getCause() != null && e.getCause().getMessage() != null
            && e.getCause().getMessage().contains("Permission denied")) {
            return true;
        }
        return false;
    }

    public static boolean isEndWithPathSeperator(String dirName) {
        if (dirName.endsWith("/")) {
            return true;
        }
        if (dirName.endsWith("\\")) {
            return true;
        }
        return false;
    }

    /**
     * 取回单个文件
     * @param connection
     * @param fullFileName 不能是目录
     * @param localDir
     * @return
     */
    private static File getRemoteFile(Connection connection, String fullFileName, String localDir,
                                      ITransferFileListener listener) {
        ExceptionUtils.throwIfEmpty(fullFileName, "错误:远程文件名为空");
        ExceptionUtils.throwIfEmpty(localDir, "错误:本地目录文件名为空");
        File dir = new File(localDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        SCPClient client = new SCPClient(connection);
        try {
            if (listener != null) {
                listener.transferFileMsg("正在获取文件:" + fullFileName);
            }
            client.get(fullFileName, localDir);
            fullFileName = fullFileName.replace('\\', '/');
            fullFileName = fullFileName.substring(fullFileName.lastIndexOf('/') + 1);
            return new File(isEndWithPathSeperator(localDir) ? localDir + fullFileName
                : localDir + File.separator + fullFileName);
        } catch (IOException e) {
            if (isNoPermissionError(e)) {
                throw new BusinessException("用户权限不够,请更换用户");
            }
            throw new UnknownException(e);
        }
    }

}
Global site tag (gtag.js) - Google Analytics