package org.jpos.jposext.isomsgaction.testing.service.support;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.SortedMap;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JOptionPane;
import junit.framework.TestSuite;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.betwixt.PatchedXMLIntrospector;
import org.apache.commons.betwixt.io.BeanReader;
import org.apache.commons.betwixt.io.read.BeanCreationChain;
import org.apache.commons.betwixt.io.read.BeanCreationList;
import org.apache.commons.betwixt.io.read.ChainedBeanCreator;
import org.apache.commons.betwixt.io.read.ElementMapping;
import org.apache.commons.betwixt.io.read.ReadContext;
import org.apache.commons.digester.Digester;
import org.jpos.iso.ISOException;
import org.jpos.iso.ISOMsg;
import org.jpos.iso.ISOPackager;
import org.jpos.iso.ISOUtil;
import org.jpos.iso.packager.XMLPackager;
import org.jpos.jposext.isomsgaction.factory.service.support.ISOMsgActionsConfigDigesterFactoryImpl;
import org.jpos.jposext.isomsgaction.helper.ISOMsgHelper;
import org.jpos.jposext.isomsgaction.helper.IsoMsgActionHelper;
import org.jpos.jposext.isomsgaction.model.validation.ValidationErrorTypeEnum;
import org.jpos.jposext.isomsgaction.service.IISOMsgAction;
import org.jpos.jposext.isomsgaction.testing.service.ITestSuiteFactory;
/**
* @author dgrandemange
*
*/
public class TestSuiteFactoryImpl implements ITestSuiteFactory {
private static final String DEFAULT_PATH_SEPARATOR = ".";
private static final String CHECK_REGEXP = "^<check:(.*)>.*$";
private static final String DEFAULT_MAPPINGS_DIR = "mappings";
private Boolean manualCheck = null;
private boolean interactive = Boolean.TRUE;
private String mappingsDirPath = DEFAULT_MAPPINGS_DIR;
private ISOPackager xmlPackager;
private String mappingTestsDirPath = "";
public TestSuiteFactoryImpl() {
super();
}
public TestSuiteFactoryImpl(String mappingsDirPath, String mappingTestsDirPath) {
super();
this.mappingsDirPath = mappingsDirPath;
this.mappingTestsDirPath = mappingTestsDirPath;
try {
xmlPackager = new XMLPackager();
} catch (ISOException e) {
// Safe to ignore
}
}
public TestSuite core(String mappingId) {
try {
List<File> mappingCfgFiles = new ArrayList<File>();
InputStream bais = IsoMsgActionHelper
.getMainISOActionConfigInputStream(mappingsDirPath,
mappingId, mappingCfgFiles);
ISOMsgActionsConfigDigesterFactoryImpl digesterFactory = new ISOMsgActionsConfigDigesterFactoryImpl();
Digester digester = digesterFactory.createDigester();
Map<String, IISOMsgAction> mapActions = (Map<String, IISOMsgAction>) digester
.parse(bais);
TestSuite rootTestSuite = new TestSuite();
rootTestSuite.setName("testMapping");
for (File mappingCfgFile : mappingCfgFiles) {
String mappingCfgName = mappingCfgFile.getName().replaceFirst(
"(^.*)\\.xml$", "$1");
String mappingCfgTestsDirPath = String.format("%s/%s",
(!("".equals(mappingTestsDirPath))) ? mappingTestsDirPath
: mappingsDirPath, mappingCfgName);
File mappingCfgTestsDir = new File(mappingCfgTestsDirPath);
if (mappingCfgTestsDir.isDirectory()) {
TestSuite mappingTestSuite = new TestSuite();
mappingTestSuite.setName(String
.format("%s", mappingCfgName));
final String testSetRegExp = "^test.*$";
File[] testSetDirs = mappingCfgTestsDir
.listFiles(new FileFilter() {
/*
* (non-Javadoc)
*
* @see java.io.FileFilter#accept(java.io.File)
*/
public boolean accept(File file) {
boolean res = false;
if (file.isDirectory()) {
res = file.getName().matches(
testSetRegExp);
}
return res;
}
});
for (File testSetDir : testSetDirs) {
MappingTest mappingTest = new MappingTest();
mappingTest.setName(String.format("%s.%s",
mappingCfgName, testSetDir.getName()));
IISOMsgAction action = mapActions.get(mappingCfgName);
if (null == action) {
throw new Exception(
String.format(
"No iso action found with id '%s'. Please check mapping config '%s/%s' : attribute 'id', in the <iso-action> element, should be set to '%s'",
mappingCfgName, mappingsDirPath,
mappingCfgFile.getName(),
mappingCfgName));
}
mappingTest.setAction(action);
// Constitution des messages ISO sources
// et injection dans le test de mapping
final String sourceMsgRegExp = "^isomsg\\.source\\.([0-9]{1,9})\\.(properties|xml)$";
File[] sourceMsgDefsFiles = testSetDir
.listFiles(new FileFilter() {
/*
* (non-Javadoc)
*
* @see
* java.io.FileFilter#accept(java.io.File)
*/
public boolean accept(File file) {
boolean res = false;
if (file.isFile()) {
res = (file.getName()
.matches(sourceMsgRegExp));
}
return res;
}
});
SortedMap<Integer, ISOMsg> mapSrcMsg = new TreeMap<Integer, ISOMsg>();
for (File sourceMsgDefsFile : sourceMsgDefsFiles) {
int indexMsg = Integer.parseInt(sourceMsgDefsFile
.getName().replaceFirst(sourceMsgRegExp,
"$1"));
String msgFileExt = sourceMsgDefsFile.getName()
.replaceFirst(sourceMsgRegExp, "$2");
ISOMsg currentSourceMsg = null;
if ("properties".equalsIgnoreCase(msgFileExt)) {
Properties props = new Properties();
props.load(new FileInputStream(
sourceMsgDefsFile));
currentSourceMsg = getISOMsgFromProps(props,
mappingTest);
} else if ("xml".equalsIgnoreCase(msgFileExt)) {
BufferedReader xmlMsgReader = null;
try {
currentSourceMsg = new ISOMsg();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
xmlMsgReader = new BufferedReader(
new FileReader(sourceMsgDefsFile));
String xmlLine = "";
while (null != xmlLine) {
bos.write(xmlLine.getBytes());
xmlLine = xmlMsgReader.readLine();
}
currentSourceMsg.setPackager(xmlPackager);
currentSourceMsg.unpack(bos.toByteArray());
} catch (Exception e) {
if (null != xmlMsgReader) {
xmlMsgReader.close();
}
}
}
if (null != currentSourceMsg) {
mapSrcMsg.put(new Integer(indexMsg),
currentSourceMsg);
}
}
for (ISOMsg srcIsoMsg : mapSrcMsg.values()) {
mappingTest.addSrcISOMsg(srcIsoMsg);
}
// Constitution du contexte de mapping
// et injection dans le test de mapping
Properties contextProps = new Properties();
File[] mappingContextDefsFiles = testSetDir
.listFiles(new FileFilter() {
/*
* (non-Javadoc)
*
* @see
* java.io.FileFilter#accept(java.io.File)
*/
public boolean accept(File file) {
boolean res = false;
if (file.isFile()) {
res = "context.properties"
.equals(file.getName());
}
return res;
}
});
for (File mappingContextDefsFile : mappingContextDefsFiles) {
contextProps.load(new FileInputStream(
mappingContextDefsFile));
resolveContextMappedBeans(testSetDir, contextProps);
mappingTest.setContext(new HashMap<String, Object>(
(Map) contextProps));
}
// Constitution du contexte ATTENDU de mapping
// et injection dans le test de mapping
Properties expectedContextProps = new Properties();
File[] mappingExpectedContextDefsFiles = testSetDir
.listFiles(new FileFilter() {
/*
* (non-Javadoc)
*
* @see
* java.io.FileFilter#accept(java.io.File)
*/
public boolean accept(File file) {
boolean res = false;
if (file.isFile()) {
res = "context.expected.properties"
.equals(file.getName());
}
return res;
}
});
for (File mappingExpectedContextDefsFile : mappingExpectedContextDefsFiles) {
expectedContextProps.load(new FileInputStream(
mappingExpectedContextDefsFile));
resolveContextMappedBeans(testSetDir,
expectedContextProps);
HashMap<String, Object> expectedContextMap = new HashMap<String, Object>(
(Map) expectedContextProps);
HashMap<String, Object> finalExpectedContextMap = new HashMap<String, Object>();
List<String> binaryAttrs = new ArrayList<String>();
List<String> nullAttrs = new ArrayList<String>();
Pattern patternHexa = Pattern
.compile("^<hexa:(.*)>.*$");
Pattern patternNull = Pattern
.compile("^<null:(.*)>.*$");
Pattern patternCheck = Pattern
.compile(CHECK_REGEXP);
for (Entry<String, Object> entry : expectedContextMap
.entrySet()) {
String entryKey = (String) entry.getKey();
String entryValue = (String) entry.getValue();
boolean matches;
Matcher hexaMatcher = patternHexa
.matcher(entryValue);
matches = hexaMatcher.matches();
if (matches) {
String hexWithSpace = hexaMatcher
.replaceFirst("$1");
String hexNoSpace = hexWithSpace.replace(
" ", "");
entry.setValue(hexNoSpace);
binaryAttrs.add(entryKey);
}
Matcher nullMatcher = patternNull
.matcher(entryValue);
matches = nullMatcher.matches();
if (matches) {
nullAttrs.add(entryKey);
}
Matcher checkMatcher = patternCheck
.matcher(entryValue);
matches = checkMatcher.matches();
if (matches) {
manageManualChecks(entryValue,
"context entry [" + entryKey + "]",
mappingTest, CHECK_REGEXP);
} else {
finalExpectedContextMap.put(entryKey,
entryValue);
}
}
mappingTest
.setExpectedContext(finalExpectedContextMap);
mappingTest
.setExpectedContextBinaryAttrs(binaryAttrs);
mappingTest.setExpectedContextNullAttrs(nullAttrs);
}
// Constitution du message ISO attendu
// et injection dans le test de mapping
final String expectedMsgRegExp = "^isomsg\\.expected\\.(properties|xml)$";
File[] expectedMsgDefsFiles = testSetDir
.listFiles(new FileFilter() {
/*
* (non-Javadoc)
*
* @see
* java.io.FileFilter#accept(java.io.File)
*/
public boolean accept(File file) {
boolean res = false;
if (file.isFile()) {
res = (file.getName()
.matches(expectedMsgRegExp));
}
return res;
}
});
for (File expectedMsgDefsFile : expectedMsgDefsFiles) {
String msgFileExt = expectedMsgDefsFile.getName()
.replaceFirst(expectedMsgRegExp, "$1");
ISOMsg expectedMsg = null;
if ("properties".equalsIgnoreCase(msgFileExt)) {
Properties expectedMsgProps = new Properties();
expectedMsgProps.load(new FileInputStream(
expectedMsgDefsFile));
expectedMsg = getISOMsgFromProps(
expectedMsgProps, mappingTest);
} else if ("xml".equalsIgnoreCase(msgFileExt)) {
BufferedReader xmlMsgReader = null;
try {
expectedMsg = new ISOMsg();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
xmlMsgReader = new BufferedReader(
new FileReader(expectedMsgDefsFile));
String xmlLine = "";
while (null != xmlLine) {
bos.write(xmlLine.getBytes());
xmlLine = xmlMsgReader.readLine();
}
expectedMsg.setPackager(xmlPackager);
expectedMsg.unpack(bos.toByteArray());
} catch (Exception e) {
if (null != xmlMsgReader) {
xmlMsgReader.close();
}
}
}
mappingTest.setExpectedISOMsg(expectedMsg);
}
// Constitution de la liste des erreurs attendues
// de validation, et injection dans le test de mapping
Properties validationErrorsProps = new Properties();
File[] validationErrorsDefsFiles = testSetDir
.listFiles(new FileFilter() {
/*
* (non-Javadoc)
*
* @see
* java.io.FileFilter#accept(java.io.File)
*/
public boolean accept(File file) {
boolean res = false;
if (file.isFile()) {
res = "errors.expected.properties"
.equals(file.getName());
}
return res;
}
});
Map<String, List<ValidationErrorTypeEnum>> mapExpectedErrsByIdPath = new HashMap<String, List<ValidationErrorTypeEnum>>();
for (File validationErrorsDefsFile : validationErrorsDefsFiles) {
validationErrorsProps.load(new FileInputStream(
validationErrorsDefsFile));
for (Entry<Object, Object> entry : validationErrorsProps
.entrySet()) {
String sTypeErreurValidation = (String) entry
.getKey();
String idPathSepList = (String) entry
.getValue();
ValidationErrorTypeEnum validationErrorTypeEnum = ValidationErrorTypeEnum
.valueOf(sTypeErreurValidation);
StringTokenizer tokenizer = new StringTokenizer(
idPathSepList, ",");
while (tokenizer.hasMoreTokens()) {
String currentIdPath = tokenizer
.nextToken().trim();
if (!("".equals(currentIdPath))) {
List<ValidationErrorTypeEnum> listErrsForIdPath = mapExpectedErrsByIdPath
.get(currentIdPath);
if (null == listErrsForIdPath) {
listErrsForIdPath = new ArrayList<ValidationErrorTypeEnum>();
mapExpectedErrsByIdPath.put(
currentIdPath,
listErrsForIdPath);
}
listErrsForIdPath
.add(validationErrorTypeEnum);
}
}
}
}
mappingTest
.setMapExpectedErrsByIdPath(mapExpectedErrsByIdPath);
mappingTestSuite.addTest(mappingTest);
}
rootTestSuite.addTest(mappingTestSuite);
}
}
return rootTestSuite;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void resolveContextMappedBeans(File testSetDir,
Properties contextProps) {
String xmlBeanRegExp = "^<xmlbean:(xmlpath=.*)(.*=.*[ ]*)*[ ]*>$";
String refBeanRegExp = "^<refbean:(.*)[ ]*>$";
Map<String, String> mapRefBean = new HashMap<String, String>();
for (Entry<Object, Object> entry : contextProps.entrySet()) {
String key = (String) entry.getKey();
String value = (String) entry.getValue();
if (value.matches(xmlBeanRegExp)) {
String mappingInfos = value.replaceFirst(xmlBeanRegExp, "$1");
String xmlBeanMappingFilePath = null;
Map<String, String> eltClassMap = new HashMap<String, String>();
StringTokenizer tokenizer = new StringTokenizer(mappingInfos,
" ");
while (tokenizer.hasMoreTokens()) {
String keyValuePair = tokenizer.nextToken();
String keyValuePairRegExp = "^(.*)=(.*)$";
if (keyValuePair.matches(keyValuePairRegExp)) {
String xmlElt = keyValuePair.replaceFirst(
keyValuePairRegExp, "$1");
String beanClassname = keyValuePair.replaceFirst(
keyValuePairRegExp, "$2");
if ("xmlpath".equalsIgnoreCase(xmlElt)) {
xmlBeanMappingFilePath = beanClassname;
} else {
eltClassMap.put(xmlElt, beanClassname);
}
}
}
try {
File xmlBeanMappingFile = new File(
testSetDir.getAbsolutePath()
+ System.getProperty("file.separator")
+ xmlBeanMappingFilePath);
BeanReader beanReader = new BeanReader();
PatchedXMLIntrospector patchedXmlIntrospector = new PatchedXMLIntrospector();
beanReader.setXMLIntrospector(patchedXmlIntrospector);
patchedXmlIntrospector.getConfiguration()
.setAttributesForPrimitives(false);
beanReader.getBindingConfiguration().setMapIDs(false);
ChainedBeanCreator chainedBeanCreator = new ChainedBeanCreator() {
public Object create(ElementMapping mapping,
ReadContext context, BeanCreationChain chain) {
if (byte.class.equals(mapping.getType())) {
String hexByteValue = mapping.getAttributes()
.getValue("hexa");
return new Byte(
ISOUtil.hex2byte(hexByteValue)[0]);
}
return chain.create(mapping, context);
}
};
BeanCreationList chain = BeanCreationList
.createStandardChain();
chain.insertBeanCreator(1, chainedBeanCreator);
beanReader.getReadConfiguration().setBeanCreationChain(
chain);
for (Entry<String, String> eltClassEntry : eltClassMap
.entrySet()) {
beanReader.registerBeanClass(eltClassEntry.getKey(),
Class.forName(eltClassEntry.getValue()));
}
Object obj = (Object) beanReader.parse(xmlBeanMappingFile);
contextProps.put(key, obj);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
} else if (value.matches(refBeanRegExp)) {
mapRefBean.put(key, value);
}
}
for (Entry<String, String> entry : mapRefBean.entrySet()) {
String beanPath = entry.getValue()
.replaceFirst(refBeanRegExp, "$1");
String[] splitStr = beanPath.split("\\.", 2);
try {
Object obj = PropertyUtils.getProperty(
contextProps.get(splitStr[0]), splitStr[1]);
contextProps.put(entry.getKey(), obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
protected ISOMsg getISOMsgFromProps(Properties props,
MappingTest mappingTest) {
ISOMsg res = new ISOMsg();
// D�terminer les champs composites et les cr�er
for (Entry entry : props.entrySet()) {
String rawIdPath = (String) entry.getKey();
String idPath = hexPathToDecPath(rawIdPath, DEFAULT_PATH_SEPARATOR);
String strLastAtomicId = ISOMsgHelper.findLastAtomicId(idPath);
int lastAtomicId = ISOMsgHelper
.getIntIdFromStringId(strLastAtomicId);
ISOMsg inter = res;
if (!(strLastAtomicId.equals(idPath))) {
int lastSepIndex = idPath.lastIndexOf(DEFAULT_PATH_SEPARATOR);
String pathOnly;
if (lastSepIndex > 0) {
pathOnly = idPath.substring(0, lastSepIndex);
} else {
pathOnly = idPath;
}
StringTokenizer tokenizer = new StringTokenizer(pathOnly,
DEFAULT_PATH_SEPARATOR);
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
int currentId = ISOMsgHelper.getIntIdFromStringId(token);
boolean cmpFieldExists = inter.hasField(currentId);
if (!cmpFieldExists) {
ISOMsg cmpField = new ISOMsg(currentId);
try {
inter.set(cmpField);
} catch (ISOException e) {
throw new RuntimeException(e);
}
}
inter = (ISOMsg) inter.getComponent(currentId);
}
}
boolean setted = false;
String entryValue = (String) entry.getValue();
Pattern pattern = Pattern.compile("^<hexa:(.*)>.*$");
Matcher m = pattern.matcher(entryValue);
boolean matches = m.matches();
if (matches) {
String hexWithSpace = m.replaceFirst("$1");
String hexNoSpace = hexWithSpace.replace(" ", "");
byte[] hex2byte = ISOUtil.hex2byte(hexNoSpace);
try {
inter.set(lastAtomicId, hex2byte);
setted = true;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
if (!setted) {
setFieldValue(inter, lastAtomicId, (String) entry.getValue(),
idPath, mappingTest);
}
}
return res;
}
protected String hexPathToDecPath(String rawIdPath,
String defaultPathSeparator) {
StringBuffer res = new StringBuffer();
StringTokenizer tokenizer = new StringTokenizer(rawIdPath,
defaultPathSeparator);
String sep = "";
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
int currentId = ISOMsgHelper.getIntIdFromStringId(token);
res.append(sep);
res.append(currentId);
sep = defaultPathSeparator;
}
return res.toString();
}
protected void setFieldValue(ISOMsg msg, int id, String value,
String idPath, MappingTest mappingTest) {
try {
String checkRegExp = CHECK_REGEXP;
if (value.matches(checkRegExp)) {
manageManualChecks(value, idPath, mappingTest, checkRegExp);
} else {
msg.set(id, value);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void manageManualChecks(String value, String idPath,
MappingTest mappingTest, String checkRegExp) {
if (interactive) {
if (null == manualCheck) {
Object[] options = { "Yes", "No" };
int n = JOptionPane
.showOptionDialog(
null,
"Some tests require manual checks.\n"
+ "Do you want a pop reminder per test to check ?",
"Manual checks", JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE, null, options,
options[1]);
manualCheck = new Boolean(n == 0 ? true : false);
}
}
mappingTest.setShowManualCheckReminder((null == manualCheck) ? false
: manualCheck.booleanValue());
mappingTest.addManualCheck(idPath,
value.replaceFirst(checkRegExp, "$1"));
}
/*
* (non-Javadoc)
*
* @see
* org.jpos.jposext.isomsgaction.testing.service.ITestSuiteFactory#create()
*/
public TestSuite create() {
return core(null);
}
/*
* (non-Javadoc)
*
* @see org.jpos.jposext.isomsgaction.testing.service.ITestSuiteFactory#
* createByMappingId(java.lang.String)
*/
public TestSuite createByMappingId(String id) {
return core(id);
}
public String getMappingsDirPath() {
return mappingsDirPath;
}
public void setMappingsDirPath(String mappingsDirPath) {
this.mappingsDirPath = mappingsDirPath;
}
public String getMappingTestsDirPath() {
return mappingTestsDirPath;
}
public void setMappingTestsDirPath(String mappingTestsDirPath) {
this.mappingTestsDirPath = mappingTestsDirPath;
}
public boolean isInteractive() {
return interactive;
}
public void setInteractive(boolean interactive) {
this.interactive = interactive;
}
}