1. 程式人生 > >使用java解析Infor XA ERP SystemLink請求響應報文

使用java解析Infor XA ERP SystemLink請求響應報文

    Infor XA ERP的SystemLink響應報文是一段比較複雜的xml,裡面記錄了操作是否成功的狀態以及操作結果或者錯誤說明。

   對SystemLink解析,就是從響應的xml報文裡面解析出操作結果狀態,如果操作失敗,則提取錯誤訊息。因為Infor XA ERP的SystemLink請求分為事務請求、非事務請求,所以解析也有一點點不同,具體細節這裡不再說明。直接上程式碼,看我是如何用java程式碼解析報文的

解析抽象類:

package cn.markwins.yinfor.utils.xml;

import java.util.List;

import org.xml.sax.helpers.DefaultHandler;

import cn.markwins.yinfor.utils.vo.systemlink.SystemLinkMessage;

public abstract class SystemLinkStockDefaultHandler extends DefaultHandler{
	/* 返回的訊息  */
	protected SystemLinkMessage systemLinkMessage = null;
	protected List<String> systemLinkExceptionList = null;
	public SystemLinkMessage getSystemLinkMessage() {
		return systemLinkMessage;
	}
	
	protected String preTagName = null;
}
解析非事務型別的SystemLink的xml報文:
package cn.markwins.yinfor.utils.xml;

import java.util.ArrayList;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;

import cn.markwins.yinfor.utils.common.StringTools;
import cn.markwins.yinfor.utils.vo.systemlink.SystemLinkMessage;

/**
 * @Description 無事務控制單一的SystemLink響應報文xml解析器
 * @author 李yi輝
 * @date 2016年7月5日
 */
public class SystemLinkTransactionNHandler extends SystemLinkStockDefaultHandler {
	private boolean messageTypeError = false;
	private boolean responseTag = false;
	private boolean exceptionTag = false;
	private boolean messageTag = false;
	private int errMsgIndex = 0;
	
	@Override
	public void startDocument() throws SAXException {
		systemLinkMessage = new SystemLinkMessage();
		systemLinkExceptionList = new ArrayList<>();
		systemLinkMessage.setStatus(true);
	}
	
	@Override
	public void startElement(String uri, String localName, String qName,
			Attributes attributes) throws SAXException {
		if(qName.matches("[A-Za-z0-9]+Response$")){
			responseTag = true;
			if("LoginResponse".equals(qName)){
				if("false".equals(attributes.getValue("actionSucceeded"))){
					systemLinkMessage.setStatus(false);
				}
			}
			
		}else{
			switch (qName) {
			case "Exception":
				exceptionTag = true;
				errMsgIndex = 0;
				break;
			case "Response":
				if("true".equals(attributes.getValue("hasErrors"))){
					systemLinkMessage.setStatus(false);
				}
				break;
			case "Message":
				messageTag = true;
				if("error".equals(attributes.getValue("type"))){
					messageTypeError = true;
					++errMsgIndex;
				}
				break;
			default:
				break;
			}
		}
		
		this.preTagName = qName;
	}
	
	@Override
	public void characters(char[] ch, int start, int length)
			throws SAXException {
		//異常資訊節點
		if(messageTypeError && messageTag && exceptionTag && "Text".equals(this.preTagName)){
			String text = null;
			if(!responseTag){	//還沒進入任何Response就出現異常資訊,則表示SystemLink伺服器異常,處理失敗
				systemLinkMessage.setStatus(false);
			}
			if(!systemLinkMessage.getStatus()){
				text = new String(ch, start, length);
			}
			if(!StringTools.isNullOrWhiteSpace(text)){
//				if(systemLinkExceptionList.isEmpty()){	//只取最後一個error型別的訊息響應
//					systemLinkExceptionList.add(text);
//				}else{
//					systemLinkExceptionList.set(0, text);
//				}
				if(errMsgIndex == 2 && systemLinkExceptionList.size() < 2){	//如果有2個或2個以上的錯誤,則第一個錯誤肯定是泛泛總結描述有錯誤
					systemLinkExceptionList.set(0, text);
				}else{					//其它情況,則是具體的錯誤資訊
					systemLinkExceptionList.add(text);
				}
			}
		}
	}

	@Override
	public void endElement(String uri, String localName, String qName)
			throws SAXException {
		if(qName.matches("[A-Za-z0-9]+Response$")){
			responseTag = false;
		}else{
			switch (qName) {
			case "Message":
				messageTag = false;
				messageTypeError = false;
				break;
			case "Exception":
				exceptionTag = false;
				break;
			default:
				break;
			}
		}
		
		this.preTagName = null;
	}
	
	@Override
	public void endDocument() throws SAXException {
		if(!systemLinkMessage.getStatus()){
			systemLinkMessage.setSystemLinkExceptionList(systemLinkExceptionList);
		}
	}

}

解析事務型別的SystemLink響應的xml報文:

package cn.markwins.yinfor.utils.xml;

import java.util.ArrayList;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;

import cn.markwins.yinfor.utils.common.StringTools;
import cn.markwins.yinfor.utils.vo.systemlink.SystemLinkMessage;

/**
 * @Description 事務控制的SystemLink響應報文xml解析器
 * @author 李yi輝
 * @date 2016年7月5日
 */
public class SystemLinkTransactionYHandler extends SystemLinkStockDefaultHandler {
	private boolean messageTypeError = false;
	private boolean responseTag = false;
	private boolean exceptionTag = false;
	private boolean messageTag = false;
	private int errMsgIndex = 0;
	private String responseName = null;
	private Integer txReqIndex = null;
	
	@Override
	public void startDocument() throws SAXException {
		systemLinkMessage = new SystemLinkMessage();
		systemLinkExceptionList = new ArrayList<>();
		systemLinkMessage.setStatus(true);
	}
	
	@Override
	public void startElement(String uri, String localName, String qName,
			Attributes attributes) throws SAXException {
		if(qName.matches("[A-Za-z0-9]+Response$")){
			responseTag = true;
			if("LoginResponse".equals(qName)){
				if("false".equals(attributes.getValue("actionSucceeded"))){
					systemLinkMessage.setStatus(false);
				}
			}else{
				responseName = attributes.getValue("name");
				if(!StringTools.isNullOrWhiteSpace(responseName)){
					int dashIndex = responseName.lastIndexOf('_');
					if(dashIndex > 1){
						txReqIndex = Integer.valueOf(responseName.substring(dashIndex + 1, responseName.length()));
					}
				}
			}
		}else{
			switch (qName) {
			case "Exception":
				exceptionTag = true;
				errMsgIndex = 0;
				break;
			case "Response":
				if("true".equals(attributes.getValue("hasErrors"))){
					systemLinkMessage.setStatus(false);
				}
				break;
			case "Message":
				messageTag = true;
				if("error".equals(attributes.getValue("type"))){
					messageTypeError = true;
					++errMsgIndex;
				}
				break;
			default:
				break;
			}
		}
		
		this.preTagName = qName;
	}
	
	@Override
	public void characters(char[] ch, int start, int length)
			throws SAXException {
		//異常資訊節點
		if(messageTypeError && messageTag && exceptionTag && "Text".equals(this.preTagName)){
			String text = null;
			//還沒進入任何Response就出現異常資訊,則表示SystemLink伺服器異常,處理失敗
			if(!responseTag){
				systemLinkMessage.setStatus(false);
			}
			//systemLink響應錯誤訊息
			if(!systemLinkMessage.getStatus()){
				text = new String(ch, start, length);
			}
			if(!StringTools.isNullOrWhiteSpace(text)){
				if(errMsgIndex == 2 && systemLinkExceptionList.size() < 2){	//如果有2個或2個以上的錯誤,則第一個錯誤肯定是泛泛總結描述有錯誤
					systemLinkExceptionList.set(0, text);
				}else{					//其它情況,則是具體的錯誤資訊
					if(txReqIndex != null && txReqIndex > 0){
						text = txReqIndex + ":" + text;
					}
					systemLinkExceptionList.add(text);
				}
			}
		}
	}

	@Override
	public void endElement(String uri, String localName, String qName)
			throws SAXException {
		if(qName.matches("[A-Za-z0-9]+Response$")){
			responseTag = false;
			responseName = null;
			txReqIndex = null;
		}else{
			switch (qName) {
			case "Message":
				messageTag = false;
				messageTypeError = false;
				break;
			case "Exception":
				exceptionTag = false;
				break;
			default:
				break;
			}
		}
		
		this.preTagName = null;
	}
	
	@Override
	public void endDocument() throws SAXException {
		if(!systemLinkMessage.getStatus()){
			systemLinkMessage.setSystemLinkExceptionList(systemLinkExceptionList);
		}
	}

}

訊息處理:

package cn.markwins.yinfor.utils.xml;

import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.log4j.Logger;

import cn.markwins.yinfor.utils.common.StreamTools;
import cn.markwins.yinfor.utils.common.StringTools;
import cn.markwins.yinfor.utils.vo.systemlink.SystemLinkMessage;

/**
 * @Description SystemLink響應訊息解析器
 * @author 李yi輝
 * @date 2016年7月6日
 */
public class SystemLinkRespMessageTools {
	
	private static Logger logger = Logger.getLogger(SystemLinkRespMessageTools.class);
	
	/**
	 * @Description 解析systemLink訊息響應
	 * @param systemLinkRespXML systemLink的xml響應訊息
	 * @param transactionFlag 解析方式
	 * 							true:事務型別systemLink xml響應
	 * 							false:非事務型別systemLink xml響應
	 * @return SystemLinkMessage 解析後的訊息封裝
	 */
	public static SystemLinkMessage parseStockSystemLinkRespXML(String systemLinkRespXML, boolean transactionFlag) {
		SystemLinkMessage systemLinkMessage = new SystemLinkMessage();
		systemLinkMessage.setStatus(false);
		List<String> systemLinkExceptionList = new ArrayList<>();
		
		//長時間無響應
		if(StringTools.isNullOrWhiteSpace(systemLinkRespXML)){
			systemLinkExceptionList.add("XA系統長時間無響應,請於Infor XA系統檢查是否已過賬,並反饋系統管理員");
			systemLinkMessage.setSystemLinkExceptionList(systemLinkExceptionList);
			logger.error("Infor XA SystemLink 長時間無響應,無響應報文");
			return systemLinkMessage;
		}
		
		//響應報文非標準化的xml,sax無法解析,特殊處理
		/*1、com.pjx.xsaa.entry.ServerNotFoundException*/
		if(systemLinkRespXML.contains("com.pjx.xsaa.entry.ServerNotFoundException")){
			systemLinkExceptionList.add("過賬失敗,Infor XA SystemLink Server未開啟,請反饋系統管理員");
			systemLinkMessage.setSystemLinkExceptionList(systemLinkExceptionList);
			return systemLinkMessage;
		}
		
		//解析報文
		try {
			SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
			SAXParser saxParser = saxParserFactory.newSAXParser();
			SystemLinkStockDefaultHandler handler = null;
			
			if(transactionFlag){
				handler = new SystemLinkTransactionYHandler();	//開啟systemLink事務的解析器
			}else{
				handler = new SystemLinkTransactionNHandler();	//沒啟SystemLink事務的解析器
			}
			
			saxParser.parse(StreamTools.getInputStreamFromString(systemLinkRespXML), handler);
			SystemLinkMessage systemLinkMessageParsed = handler.getSystemLinkMessage();
			if(systemLinkMessageParsed == null || systemLinkMessageParsed.getStatus() == null){
				systemLinkExceptionList.add("系統異常,請於XA ERP檢查是否已過賬,並反饋系統管理員");
				systemLinkMessage.setSystemLinkExceptionList(systemLinkExceptionList);
				logger.error("SystemLink訊息解析異常,無法提取解析狀態");
				return systemLinkMessage;
			}
			
			systemLinkMessage.setStatus(systemLinkMessageParsed.getStatus());
			systemLinkMessage.setSystemLinkExceptionList(systemLinkMessageParsed.getSystemLinkExceptionList());
		} catch (Exception e) {
			systemLinkMessage.setStatus(false);
			systemLinkExceptionList.add("系統異常,請於XA ERP檢查是否已過賬,並反饋系統管理員");
			systemLinkMessage.setSystemLinkExceptionList(systemLinkExceptionList);
			logger.error("系統異常,請於Infor XA系統檢查是否已過賬,並反饋系統管理員--SystemLink訊息解析異常", e);
		} 
		
		return systemLinkMessage;
	}
}

通用的傳送、解析工具

package cn.markwins.yinfor.utils.net;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.log4j.Logger;
import org.junit.Test;

import cn.markwins.yinfor.cas.domain.User;
import cn.markwins.yinfor.global.GlobalParameters;
import cn.markwins.yinfor.sys.domain.SystemLinkFileBean;
import cn.markwins.yinfor.utils.common.ConfigFileTools;
import cn.markwins.yinfor.utils.common.FileTools;
import cn.markwins.yinfor.utils.common.ReflectTools;
import cn.markwins.yinfor.utils.common.StringTools;
import cn.markwins.yinfor.utils.vo.response.CommonMessage;
import cn.markwins.yinfor.utils.vo.response.CommonMessageType;
import cn.markwins.yinfor.utils.vo.systemlink.SystemLinkMessage;
import cn.markwins.yinfor.utils.xml.SystemLinkRespMessageTools;

/**
 * @Description 傳送SystemLink請求
 * @author 李yi輝
 * @date 2016年6月24日
 */
public class SystemLinkTools {

	private static Logger logger = Logger.getLogger(SystemLinkTools.class);
	
	/**
	 * @Description 單一systemLink請求
	 * @param user 登陸使用者
	 * @param bean 業務bean
	 * @param systemLinkXMLFilePath systemLink請求的xml檔案路徑
	 * @return CommonMessage<String> 請求響應
	 */
	public static CommonMessage<String> postSystemLinkNoTransactionRequest(User user, Object bean, String systemLinkXMLFilePath){
		CommonMessage<String> commonMsg = new CommonMessage<>();
		commonMsg.setStatus(false);
		commonMsg.setType(CommonMessageType.E);
		
		//1、判斷檔案、業務bean是否有資料
		if(user == null || StringTools.isNullOrWhiteSpace(systemLinkXMLFilePath)){
			commonMsg.setErrCode("SYS50901");
			commonMsg.setMessage("空的xml檔案或使用者登入資訊" + systemLinkXMLFilePath + " " + (bean != null ? bean.getClass().getSimpleName() : ""));
			return commonMsg;
		}
		boolean loginRequest = false;
		if("/SystemLink_Login.xml".equals(systemLinkXMLFilePath)){
			loginRequest = true;
		}else{
			if(bean == null){
				commonMsg.setErrCode("SYS50901");
				commonMsg.setMessage("業務bean資訊為空" + systemLinkXMLFilePath);
				return commonMsg;
			}
		}
		
		//2、讀取systemLink xml檔案
		String systemLinkXMLFile = FileTools.readFileContent(systemLinkXMLFilePath);
		if(StringTools.isNullOrWhiteSpace(systemLinkXMLFile)){
			commonMsg.setErrCode("SYS50901");
			commonMsg.setMessage("讀取" + systemLinkXMLFilePath +"檔案內容失敗");
			return commonMsg;
		}
		
		//3、將登陸使用者及業務bean資訊設定到xml裡面對應的佔位符
		String xmlAfterReg = mulSetter(systemLinkXMLFile, user, bean, loginRequest);
		if(StringTools.isNullOrWhiteSpace(xmlAfterReg) || hasSystemLinkBeanReg(xmlAfterReg)){
			commonMsg.setErrCode("SYS50901");
			commonMsg.setMessage("提取檔案屬性失敗:" + systemLinkXMLFilePath + ":" + (bean != null ? bean.getClass().getSimpleName() : "NULL"));
			return commonMsg;
		}
		
		//4、向XA SystemLink server 傳送 Http Post請求
		String currentTimeID = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
		logger.info(currentTimeID + "----------SystemLink request報文:\n" + xmlAfterReg);
		String systemLinkServerURL = ConfigFileTools.getCfgValue(GlobalParameters.SYSTEMLINK_FILE, GlobalParameters.SYSTEMLINK_URL);
		if(StringTools.isNullOrWhiteSpace(systemLinkServerURL)){
			systemLinkServerURL = "http://192.168.0.10:36001/SystemLink/servlet/SystemLinkServlet";
		}
		Map<Boolean, String> postStatusMap = HttpTools.postXMLRequest(systemLinkServerURL, xmlAfterReg);
		if(postStatusMap == null || postStatusMap.isEmpty()){
			commonMsg.setErrCode("SYS50902");
			commonMsg.setMessage("SLI Server地址錯誤或xml空報文");
			return commonMsg;
		}
		if(!postStatusMap.containsKey(true)){	//響應失敗http code
			commonMsg.setErrCode("SYS50902");
			commonMsg.setMessage(postStatusMap.get(false));
			return commonMsg;
		}
		String systemLinkRespXML = postStatusMap.get(true);
		logger.info(currentTimeID + "----------SystemLink response報文:\n" + systemLinkRespXML);
		
		//5、解析systemLink響應
		SystemLinkMessage systemLinkMessage = SystemLinkRespMessageTools.parseStockSystemLinkRespXML(systemLinkRespXML, false);
		if(systemLinkMessage == null){
			commonMsg.setErrCode("SYS50903");
			commonMsg.setMessage("提取SystemLink響應狀態異常,請通知系統管理員");
			return commonMsg;
		}
		if(!systemLinkMessage.getStatus()){	//SystemLink結果請求響應,失敗原因
			commonMsg.setErrCode("SYS50999");
			List<String> systemLinkExceptionList = systemLinkMessage.getSystemLinkExceptionList();
			if(systemLinkExceptionList != null && !systemLinkExceptionList.isEmpty()){
				StringBuffer systemLinkErrMsgBuf = new StringBuffer();
				for(String systemLinkErrMsg : systemLinkExceptionList){
					if(!StringTools.isNullOrWhiteSpace(systemLinkErrMsg)){
						systemLinkErrMsgBuf.append("\n<br>" + systemLinkErrMsg);
					}
				}
				commonMsg.setMessage(systemLinkErrMsgBuf.toString());
			}
		}
		
		//6、沒有異常,設定systemLink請求響應的成功碼
		if(StringTools.isNullOrWhiteSpace(commonMsg.getErrCode())){
			commonMsg.setStatus(true);
			commonMsg.setType(CommonMessageType.I);
		}
		
		return commonMsg;
	}
	
	/**
	 * @Description 將使用者登入資訊及業務bean資訊設定到對應的xml佔位符裡面
	 * @param systemLinkXMLFile 請求的systemLink xml檔案
	 * @param user 登入使用者資訊
	 * @param bean 業務bean
	 * @param loginRequest 是否登入請求,當是登入請求,可以不用考慮業務bean
	 * @return String 替換後的xml
	 */
	private static String mulSetter(String systemLinkXMLFile, User user, Object bean, boolean loginRequest){
		boolean beanOK = true;
		if(!loginRequest && bean == null){	//登入請求可以不設定bean,其他情況的SystemLink請求必須設定對應的bean
			beanOK = false;
		} 
		if(beanOK && user != null && !StringTools.isNullOrWhiteSpace(systemLinkXMLFile)){
			//1、業務資料設定
			User cloneUser = null;
			Object cloneBean = null;
			try {
				cloneUser =  (User) BeanUtils.cloneBean(user);
				if(!("1".equals(cloneUser.getLoginType()))){	//非XA登入,取預設使用者名稱及密碼
					String systemUser = ConfigFileTools.getCfgValue(GlobalParameters.SYSTEMLINK_FILE, GlobalParameters.SYSTEMLINK_USER);
					String systemPassword = ConfigFileTools.getCfgValue(GlobalParameters.SYSTEMLINK_FILE, GlobalParameters.SYSTEMLINK_PASSWORD);
					if(!StringTools.isNullOrWhiteSpace(systemUser) && !StringTools.isNullOrWhiteSpace(systemPassword)){
						cloneUser.setUsridk(systemUser);
						cloneUser.setLoginPassword(systemPassword);
					}
				}
				if(bean != null){
					cloneBean = BeanUtils.cloneBean(bean);
				}
			} catch (Exception e) {
				logger.error("SystemLink單一請求方式,拷貝物件失敗", e);
				return systemLinkXMLFile;
			} 
			//2、替換所有 &%filed%& 的佔位符
			StringBuffer systemLinkXMLFileBuf = new StringBuffer();
			Pattern p = Pattern.compile("&%[a-zA-z0-9\\.]+%&");
			Matcher m = p.matcher(systemLinkXMLFile);
			while(m.find()){
				String regVariable = m.group();
				String filed = regVariable.substring(2,regVariable.length() - 2);
				if(filed.startsWith("user.")){	//登入使用者的值
					filed = filed.split("\\.")[1];
					m.appendReplacement(systemLinkXMLFileBuf, ReflectTools.invokeGetter(cloneUser, filed).toString());
				}else{ //業務bean的值
					Object relatedObj = cloneBean;
					String relatedFiled = filed;
					//先檢視是否related關聯物件屬性, ex:relatedCustomerOrderExtension.fdcust
					Pattern rp = Pattern.compile("^related[a-zA-Z0-9]+(\\.[a-zA-Z0-9]+){1,}");
					Matcher rm = rp.matcher(filed);
					if(rm.matches()){
						String[] splitRelated = filed.substring(7).split("\\.");
						int length = splitRelated.length;
						int index = 0;
						for(String s : splitRelated){
							++index;
							if(index < length){	//非最後1個,找關聯物件
								relatedObj = ReflectTools.invokeGetter(relatedObj, StringTools.firstLetterToLow(s));
							}else{	//最後1個了,找屬性
								relatedFiled = s.trim();
							}
						}
					}
					//然後找最後物件->屬性的值
					Object filedObject = null;
					String filedObjectValue = "";
					if(relatedObj != null && relatedFiled != null){
						filedObject = ReflectTools.invokeRelatedGetter(relatedObj, relatedFiled);
						if(filedObject != null){
							if(filedObject instanceof java.util.Date){	/* 日期轉換為yyyyMMdd格式 */
								SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd");
								filedObjectValue = sf.format(filedObject);
							}else if(filedObject instanceof java.lang.Boolean){
								if((boolean) filedObject){	/* true->1、false->0 */
									filedObjectValue = "1";
								}else{
									filedObjectValue = "0";
								}
							}else{
								filedObjectValue = filedObject.toString();
							}
						}
					}
					
					//通配佔位符值設定
					m.appendReplacement(systemLinkXMLFileBuf, filedObjectValue);
				}
			}
			m.appendTail(systemLinkXMLFileBuf);
			
			//4、返回替換值後的systemLink請求的xml報文
			return systemLinkXMLFileBuf.toString();
		}
		
		return systemLinkXMLFile;
	}
	
	/**
	 * @Description 一次傳送多個systemLink請求,且啟動事務管理 
	 *	傳送systemLink請求分6個步驟
	 *		1、先準備SystemLink xml模板即xml萬用字元對應的佔位符業務bean
	 *		2、然後讀取 SystemLink xml檔案內容
	 *		3、bean屬性值替換佔位符
	 *		4、最後讀取 </Request> xml結束標籤 及登出內容,結果新增到前面所有的請求xml後面
	 *		5、傳送SystemLink請求,請求內容即為上述的整個xml
	 *      6、處理響應
	 * @param systemLinkXMLFileList SystemLink xml檔案列表及各檔案對應的bean
	 * @return CommonMessage<String> 響應結果
	 */
	public static CommonMessage<String> postSystemLinkTransactionRequest(ArrayList<SystemLinkFileBean> systemLinkXMLFileList){
		CommonMessage<String> commonMsg = new CommonMessage<>();
		commonMsg.setStatus(false);
		commonMsg.setType(CommonMessageType.E);
		
		//1、讀取systemlLink xml請求模板檔案,並根據map的bean屬性替換對應的佔位符的值
		if(systemLinkXMLFileList == null || systemLinkXMLFileList.isEmpty()){	//至少有1個登入的systemLink xml
			commonMsg.setErrCode("SYS50901");
			commonMsg.setMessage("沒有指定任何xml請求檔案列表");
			return commonMsg;
		}
		StringBuffer xmlFileBuf = new StringBuffer();
		boolean logoutXMLExists = false;
		for(SystemLinkFileBean systemLinkFileBean : systemLinkXMLFileList){
			if(systemLinkFileBean == null || !systemLinkFileBean.isActi()){
				commonMsg.setErrCode("SYS50901");
				commonMsg.setMessage("空的xml檔案或業務資料" + (systemLinkFileBean != null ? systemLinkFileBean.getXmlFilePath() : ""));
				return commonMsg;
			}
			//1.1、讀取xml檔案的內容
			String systemLinkXMLFilePath = systemLinkFileBean.getXmlFilePath();
			String curSystemLinkXMLFile = FileTools.readFileContent(systemLinkXMLFilePath);
			if(StringTools.isNullOrWhiteSpace(curSystemLinkXMLFile)){
				commonMsg.setErrCode("SYS50901");
				commonMsg.setMessage("讀取" + systemLinkXMLFilePath +"檔案內容失敗");
				return commonMsg;
			}
			if(GlobalParameters.SYSTEMLINK_LOGOUT.equals(systemLinkXMLFilePath)){
				logoutXMLExists = true;
			}
			//1.2、提取bean屬性,設定到xml檔案對應通配佔位符的值
			Object bean = systemLinkFileBean.getBean();
			int txReqIndex = systemLinkFileBean.getTxReqIndex();
			String xmlAfterReg = singleSetter(curSystemLinkXMLFile, bean, txReqIndex) + "\n";
			if(StringTools.isNullOrWhiteSpace(xmlAfterReg) || hasSystemLinkBeanReg(xmlAfterReg)){
				commonMsg.setErrCode("SYS50901");
				commonMsg.setMessage("提取檔案屬性失敗:" + systemLinkXMLFilePath + ":" + (bean != null ? bean.getClass().getSimpleName() : "NULL"));
				return commonMsg;
			}
			xmlFileBuf.append(xmlAfterReg);
		}
		//1.3、自動加SystemLink_Logout.xml登出及xml關閉</標籤...
		if(!logoutXMLExists){
			String logoutSystemLinkXMLFile = FileTools.readFileContent(GlobalParameters.SYSTEMLINK_LOGOUT);
			if(StringTools.isNullOrWhiteSpace(logoutSystemLinkXMLFile)){
				commonMsg.setErrCode("SYS50901");
				commonMsg.setMessage("讀取" + logoutSystemLinkXMLFile +"檔案內容失敗");
				return commonMsg;
			}else{
				xmlFileBuf.append(logoutSystemLinkXMLFile); 
			}
		}
		String currentTimeID = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
		logger.info(currentTimeID + "----------SystemLink request報文:\n" + xmlFileBuf);
		
		//2、向XA SystemLink server 傳送 Http Post請求
		String systemLinkServerURL = ConfigFileTools.getCfgValue(GlobalParameters.SYSTEMLINK_FILE, GlobalParameters.SYSTEMLINK_URL);
		if(StringTools.isNullOrWhiteSpace(systemLinkServerURL)){
			systemLinkServerURL = "http://192.168.0.10:36001/SystemLink/servlet/SystemLinkServlet";
		}
		Map<Boolean, String> postStatusMap = HttpTools.postXMLRequest(systemLinkServerURL, xmlFileBuf.toString());
		if(postStatusMap == null || postStatusMap.isEmpty()){
			commonMsg.setErrCode("SYS50902");
			commonMsg.setMessage("SLI Server地址錯誤或xml空報文");
			return commonMsg;
		}
		if(!postStatusMap.containsKey(true)){	//響應失敗http code
			commonMsg.setErrCode("SYS50902");
			commonMsg.setMessage(postStatusMap.get(false));
			return commonMsg;
		}
		String systemLinkRespXML = postStatusMap.get(true);
		logger.info(currentTimeID + "----------SystemLink response報文:\n" + systemLinkRespXML);
		
		//3、解析響應結果
		SystemLinkMessage systemLinkMessage = SystemLinkRespMessageTools.parseStockSystemLinkRespXML(systemLinkRespXML, true);
		if(systemLinkMessage == null){
			commonMsg.setErrCode("SYS50903");
			commonMsg.setMessage("提取SystemLink響應狀態異常,請通知系統管理員");
			return commonMsg;
		}
		if(!systemLinkMessage.getStatus()){	//SystemLink結果請求響應,失敗原因
			commonMsg.setErrCode("SYS50999");
			List<String> systemLinkExceptionList = systemLinkMessage.getSystemLinkExceptionList();
			if(systemLinkExceptionList != null && !systemLinkExceptionList.isEmpty()){
				StringBuffer systemLinkErrMsgBuf = new StringBuffer();
				for(String systemLinkErrMsg : systemLinkExceptionList){
					if(!StringTools.isNullOrWhiteSpace(systemLinkErrMsg)){
						systemLinkErrMsgBuf.append("\n<br>" + systemLinkErrMsg);
					}
				}
				commonMsg.setMessage(systemLinkErrMsgBuf.toString());
			}
		}
		
		//4、沒有異常,設定systemLink請求響應的成功碼
		if(StringTools.isNullOrWhiteSpace(commonMsg.getErrCode())){
			commonMsg.setStatus(true);
			commonMsg.setType(CommonMessageType.I);
		}
		
		return commonMsg;
	}
	
	/**
	 * @Description 將systemLink xml裡面所有萬用字元 &%屬性%& 用對應屬性值替代
	 * @param systemLinkXMLFile
	 * @param bean 從此bean中找對應屬性的值
	 * @return String 處理後的systemLink xml
	 */
	private static String singleSetter(String systemLinkXMLFile, Object bean, int txReqIndex){
		if(bean != null){
			//1、bean copy->realBean,引用不影響原來的物件值
			Object cloneBean = null;
			try {
				cloneBean = BeanUtils.cloneBean(bean);
			} catch (Exception e) {
				logger.error("拷貝物件失敗:" + bean.getClass().getName(), e);
				return systemLinkXMLFile;
			} 
			
			//2、使用者登入方式處理
			if(cloneBean != null && cloneBean instanceof User){
				User user = (User) cloneBean;
				if(!("1".equals(user.getLoginType()))){	//非XA登入方式,取預設使用者名稱及密碼
					String systemUser = ConfigFileTools.getCfgValue(GlobalParameters.SYSTEMLINK_FILE, GlobalParameters.SYSTEMLINK_USER);
					String systemPassword = ConfigFileTools.getCfgValue(GlobalParameters.SYSTEMLINK_FILE, GlobalParameters.SYSTEMLINK_PASSWORD);
					if(!StringTools.isNullOrWhiteSpace(systemUser) && !StringTools.isNullOrWhiteSpace(systemPassword)){
						user.setUsridk(systemUser);
						user.setLoginPassword(systemPassword);
					}
				}
			}
			
			//3、通過反射將bean屬性值替換成佔位符
			if(cloneBean != null){
				//3-1、正則通配佔位符
				StringBuffer systemLinkXMLFileBuf = new StringBuffer();
				Pattern p = Pattern.compile("&%[a-zA-z0-9\\.]+%&");
				Matcher m = p.matcher(systemLinkXMLFile);
				//3-2、替換所有 &%filed%& 的佔位符
				while(m.find()){
					String regVariable = m.group();
					String filed = regVariable.substring(2,regVariable.length() - 2);
					if(!StringTools.isNullOrWhiteSpace(filed)){
/*						
						if(filed.startsWith("_RequestName")){ //請求name,在一個systemLink裡面不能重複,ex:Create name = 'createWarehouseLocation'
							String beanName = cloneBean.getClass().getSimpleName();
							String curMillisecond = new SimpleDateFormat("ssSSS").format(new Date()) + (int) (Math.random()*100);
							String requestName = systemLinkXMLFile.substring(0, m.start()-7).trim();
							String _RequestName = requestName + beanName + curMillisecond;
							System.out.println("xml_RequestName:" + _RequestName);
							m.appendReplacement(systemLinkXMLFileBuf, _RequestName);
*/
						if("txReqIndex".equals(filed)){ //請求name,在一個systemLink裡面不能重複,ex:Create name = 'createWarehouseLocation'
							m.appendReplacement(systemLinkXMLFileBuf,"_" + txReqIndex);
						}else{	//找對應bean的屬性
							Object relatedObj = cloneBean;
							String relatedFiled = filed;
							//先檢視是否related關聯物件屬性, ex:relatedCustomerOrderExtension.fdcust
							Pattern rp = Pattern.compile("^related[a-zA-Z0-9]+(\\.[a-zA-Z0-9]+){1,}");
							Matcher rm = rp.matcher(filed);
							if(rm.matches()){
								String[] splitRelated = filed.substring(7).split("\\.");
								int length = splitRelated.length;
								int index = 0;
								for(String s : splitRelated){
									++index;
									if(index < length){	//非最後1個,找關聯物件
										relatedObj = ReflectTools.invokeGetter(relatedObj, StringTools.firstLetterToLow(s));
									}else{	//最後1個了,找屬性
										relatedFiled = s.trim();
									}
								}
							}
							//然後找最後物件->屬性的值
							Object filedObject = null;
							String filedObjectValue = "";
							if(relatedObj != null && relatedFiled != null){
								filedObject = ReflectTools.invokeRelatedGetter(relatedObj, relatedFiled);
								if(filedObject != null){
									if(filedObject instanceof java.util.Date){	/* 日期轉換為yyyyMMdd格式 */
										SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd");
										filedObjectValue = sf.format(filedObject);
									}else if(filedObject instanceof java.lang.Boolean){
										if((boolean) filedObject){	/* true->1、false->0 */
											filedObjectValue = "1";
										}else{
											filedObjectValue = "0";
										}
									}else{
										filedObjectValue = filedObject.toString();
									}
								}
							}
							
							//通配佔位符值設定
							m.appendReplacement(systemLinkXMLFileBuf, filedObjectValue);
						}	
					}
				}
				m.appendTail(systemLinkXMLFileBuf);
				
				//3-3、返回替換通配佔位符的結果
				return systemLinkXMLFileBuf.toString();
			}
			
		}
		
		return systemLinkXMLFile;
	}
	
	/**
	 * @Description 校驗xml內容裡面是否還有未處理完的佔位萬用字元
	 * @param systemLinkXMLFile 待校驗的xml內容
	 * @return boolean
	 */
	private static boolean hasSystemLinkBeanReg(String systemLinkXMLFile){
		if(!StringTools.isNullOrWhiteSpace(systemLinkXMLFile)){
			Pattern p = Pattern.compile("&%[a-zA-z0-9\\.]*%&");
			Matcher m = p.matcher(systemLinkXMLFile);
			if(m.find()){
				return true;
			}
		}
		return false;
	}
	
	/**
	 * @author terrylee
	 * @Description SystemLink唯一碼產生機制,對映規則如下
	 * @return String SystemLink唯一碼,10位數字
	 *	字符集	0	1	2	3	4	5	6	7	8	9	A	B	C	D	E	F	G	H	I	J	K	L	M	N	O	P	Q	R	S	T	U	V	W	X	Y	Z	{	}	[	]	(	)	\	/	&	^	~	:	#	$	!	|	+	-	?	.	,	@	`	_
	 *	年份 1位								   			2016	2017	2018	2019	2020	2021	2022	2023	2024	2025	2026	2027	2028	2029	2030	2031	2032	2033	2034	2035	2036	2037	2038	2039	2040	2041	2042	2043	2044	2045	2046	2047	2048	2049	2050	2051	2052	2053	2054	2055	2056	2057	2058	2059	2060	2061	2062	2063	2064	2065
	 *	月份 1位		1	2	3	4	5	6	7	8	9	10	11	12																																															
	 *	日期 1位		1	2	3	4	5	6	7	8	9	10	11	12	13	14	15	16	17	18	19	20	21	22	23	24	25	26	27	28	29	30	31																												
	 *	小時 1位	0	1	2	3	4	5	6	7	8	9	10	11	12	13	14	15	16	17	18	19	20	21	22	23																																				
	 *	分鐘 1位	0	1	2	3	4	5	6	7	8	9	10	11	12	13	14	15	16	17	18	19	20	21	22	23	24	25	26	27	28	29	30	31	32	33	34	35	36	37	38	39	40	41	42	43	44	45	46	47	48	49	50	51	52	53	54	55	56	57	58	59
	 *	秒數 1位	0	1	2	3	4	5	6	7	8	9	10	11	12	13	14	15	16	17	18	19	20	21	22	23	24	25	26	27	28	29	30	31	32	33	34	35	36	37	38	39	40	41	42	43	44	45	46	47	48	49	50	51	52	53	54	55	56	57	58	59
	 *	毫秒 3位	0	1	2	3	4	5	6	7	8	9																																																		
	 *	校驗位或隨機數 1位	0	1	2	3	4	5	6	7	8	9																																																		
	 * @throws Exception 
	 */
	@SuppressWarnings("static-access")
	@Test
	public static String getSystemLinkSeq(){			   
		StringBuffer systemLinkSeqBuf = new StringBuffer();
												   //01234567890123456
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
 		String currentDate = sdf.format(new Date());
 		String currentYear = currentDate.substring(0, 4);
 		String currentMonth = currentDate.substring(4, 6);
 		String currentDay = currentDate.substring(6, 8);
 		String currentHour = currentDate.substring(8, 10);
 		String currentMinute = currentDate.substring(10, 12);
 		String currentSecond = currentDate.substring(12, 14);
 		String currentMillisecond = currentDate.substring(14);
		
 		//年 
 		int year = Integer.valueOf(currentYear);
 		if(year < 2016) year = 2016;
 		char yearMapping = 'A';  //2016對應A
 		if(year < 2042){	//2016-2041,按照ascii符規格自動計算
 	 		yearMapping = (char) (yearMapping + (year - 2016));
 		}else{	//2042後雜湊無規則的ascii符
 			yearMapping = asciiMapping(year);
 		}
 		systemLinkSeqBuf.append(yearMapping);
 		
 		//月、日、時、分、秒
 		systemLinkSeqBuf.append(seqMapping(currentMonth))
 			.append(seqMapping(currentDay))
 			.append(seqMapping(currentHour))
 			.append(seqMapping(currentMinute))
 			.append(seqMapping(currentSecond));
 		//毫秒
 		systemLinkSeqBuf.append(currentMillisecond);
 		//隨機數
 		systemLinkSeqBuf.append((int)(Math.random()*10));
 		try {
			Thread.currentThread().sleep(1); //睡眠1毫秒,保證同一單據的各項程式批量產生時毫秒級別不重複
		} catch (InterruptedException e) {
			logger.error("生成SystemLink唯一Token碼時,發生中斷異常", e);
		}
 		return systemLinkSeqBuf.toString();
	}
	
	/**
	 * @Description 月、日、時、分、秒 對映
	 * @param num 月、日、時、分、秒
	 * @return char 根據規則,月、日、時、分、秒對映後的字元
	 */
	private static char seqMapping(String num){
		if(StringTools.isNullOrWhiteSpace(num) || num.trim().length() != 2){
			return '0';
		}
		//01、02....09時
		if(num.startsWith("0")){
			return num.charAt(1);
		}
		//10後
		char mapped = 'A';
		int dateNum = Integer.valueOf(num);
		if(dateNum < 36){	//10-35,按照ascii符規格自動計算
			mapped = (char) (mapped + (dateNum - 10));
		}else{	//36後雜湊無規則的ascii符
			mapped = asciiMapping(dateNum);
		}
		
		return mapped;
	}
	
	/**
	 * @Description 根據雜湊規則,將源數字匹配到指定的ascii符
	 * {	}	     [	    ]	    (	    )	    %	     /	    &	    ^	    <	    :	    #	    >	    !	    |	    +	    -	    ?	    .	    ,	    @	    `	    _
     * 2042	2043	2044	2045	2046	2047	2048	2049	2050	2051	2052	2053	2054	2055	2056	2057	2058	2059	2060	2061	2062	2063	2064	2065
     * 36	37	     38	    39	    40	    41	    42	    43	    44	    45	    46	    47	    48	    49	    50	    51	    52	    53	    54	    55	    56	    57	    58	    59
	 * @param num 來源數字
	 * @return char 對映後的ascii碼符
	 */
	private static char asciiMapping(int num){
		char mapped = '{';
		if(num == 2043 || num == 37){
			mapped = '}';
		}else if(num == 2044 || num == 38){
			mapped = '[';
		}else if(num == 2045 || num == 39){
			mapped = ']';
		}else if(num == 2046 || num == 40){
			mapped = '(';
		}else if(num == 2047 || num == 41){
			mapped = ')';
		}else if(num == 2048 || num == 42){
			mapped = '%';
		}else if(num == 2049 || num == 43){
			mapped = '/';
		}else if(num == 2050 || num == 44){
			mapped = '&';
		}else if(num == 2051 || num == 45){
			mapped = '^';
		}else if(num == 2052 || num == 46){
			mapped = '<';
		}else if(num == 2053 || num == 47){
			mapped = ':';
		}else if(num == 2054 || num == 48){
			mapped = '#';
		}else if(num == 2055 || num == 49){
			mapped = '>';
		}else if(num == 2056 || num == 50){
			mapped = '!';
		}else if(num == 2057 || num == 51){
			mapped = '|';
		}else if(num == 2058 || num == 52){
			mapped = '+';
		}else if(num == 2059 || num == 53){
			mapped = '-';
		}else if(num == 2060 || num == 54){
			mapped = '?';
		}else if(num == 2061 || num == 55){
			mapped = '.';
		}else if(num == 2062 || num == 56){
			mapped = ',';
		}else if(num == 2063 || num == 57){
			mapped = '@';
		}else if(num == 2064 || num == 58){
			mapped = '`';
		}else if(num == 2065 || num == 59){
			mapped = '_';
		}
		return mapped;
	}
}


http://blog.csdn.net/yihuiworld