![JAR search and dependency download from the Maven repository](/logo.png)
org.jpos.transaction.participant.SelectDestination Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jpos Show documentation
Show all versions of jpos Show documentation
jPOS is an ISO-8583 based financial transaction
library/framework that can be customized and
extended in order to implement financial interchanges.
/*
* jPOS Project [http://jpos.org]
* Copyright (C) 2000-2018 jPOS Software SRL
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package org.jpos.transaction.participant;
import org.jdom2.Element;
import org.jpos.core.*;
import org.jpos.iso.ISOMsg;
import org.jpos.q2.Q2;
import org.jpos.rc.CMF;
import org.jpos.transaction.Context;
import org.jpos.transaction.ContextConstants;
import org.jpos.transaction.TransactionParticipant;
import org.jpos.util.Caller;
import org.jpos.util.Log;
import org.jpos.util.LogEvent;
import org.jpos.util.Logger;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@SuppressWarnings("unused")
public class SelectDestination implements TransactionParticipant, Configurable, XmlConfigurable {
private String requestName;
private String destinationName;
private String defaultDestination;
private boolean failOnNoRoute;
private CardValidator validator;
private Set binranges = new TreeSet<>();
private List regexps = new ArrayList<>();
@Override
public int prepare(long id, Serializable context) {
Context ctx = (Context) context;
ISOMsg m = (ISOMsg) ctx.get(requestName);
boolean destinationSet = false;
if (m != null && (m.hasField(2) || m.hasField(35))) {
try {
Card card = Card.builder().validator(validator).isomsg(m).build();
String destination = getDestination(card);
if (destination != null) {
ctx.put(destinationName, destination);
destinationSet = true;
}
} catch (InvalidCardException ex) {
return ctx.getResult().fail(
CMF.INVALID_CARD_OR_CARDHOLDER_NUMBER, Caller.info(), ex.getMessage()
).FAIL();
}
}
if (!destinationSet && ctx.get(destinationName) == null)
ctx.put(destinationName, defaultDestination);
if (failOnNoRoute && ctx.get(destinationName) == null)
return ctx.getResult().fail(CMF.ROUTING_ERROR, Caller.info(), "No routing info").FAIL();
return PREPARED | NO_JOIN | READONLY;
}
public void setConfiguration (Configuration cfg) {
this.requestName = cfg.get("request", ContextConstants.REQUEST.toString());
this.destinationName = cfg.get ("destination", ContextConstants.DESTINATION.toString());
this.defaultDestination = cfg.get("default-destination", null);
this.validator = cfg.getBoolean("ignore-luhn") ?
new IgnoreLuhnCardValidator() : Card.Builder.DEFAULT_CARD_VALIDATOR;
this.failOnNoRoute = cfg.getBoolean("fail");
}
/**
* @param xml Configuration element
* @throws ConfigurationException
*
*
* SelectDestination expects an XML configuration in the following format:
*
*
* 4000000..499999
* 4550000..455999
* 5
*
*
*/
public void setConfiguration(Element xml) throws ConfigurationException {
for (Element ep : xml.getChildren("endpoint")) {
String destination = ep.getAttributeValue("destination");
StringTokenizer st = new StringTokenizer(ep.getText());
while (st.hasMoreElements()) {
BinRange br = new BinRange(destination, st.nextToken());
binranges.add(br);
}
}
for (Element re : xml.getChildren("regexp")) {
String destination = re.getAttributeValue("destination");
regexps.add(
new PanRegExp(re.getAttributeValue("destination"), re.getTextTrim())
);
}
LogEvent evt = Log.getLog(Q2.LOGGER_NAME, this.getClass().getName()).createLogEvent("config");
for (PanRegExp r : regexps)
evt.addMessage("00:" + r);
for (BinRange r : binranges)
evt.addMessage(r);
Logger.log(evt);
}
private String getDestination (Card card) {
String destination = getDestinationByRegExp(card.getPan());
if (destination == null)
destination = getDestinationByPanNumber(card.getPanAsNumber());
return destination;
}
private String getDestinationByPanNumber (BigInteger pan) {
final BigInteger p = BinRange.floor(pan);
return binranges
.stream()
.filter(r -> r.isInRange(p))
.findFirst()
.map(BinRange::destination).orElse(null);
}
private String getDestinationByRegExp (String pan) {
return regexps
.stream()
.filter(r -> r.matches(pan))
.findFirst()
.map(PanRegExp::destination).orElse(null);
}
public static class BinRange implements Comparable {
private String destination;
private BigInteger low;
private BigInteger high;
private short weight;
private static Pattern rangePattern = Pattern.compile("^([\\d]{1,19})*(?:\\.\\.)?([\\d]{0,19})?$");
/**
* Sample bin:
*
* - 4
* - 411111
* - 4111111111111111
* - 411111..422222
*
* @param destination range destination
* @param rangeExpr either a 'bin', 'extended bin' or 'bin range'
*/
BinRange(String destination, String rangeExpr) {
this.destination = destination;
Matcher matcher = rangePattern.matcher(rangeExpr);
if (!matcher.matches())
throw new IllegalArgumentException("Invalid range " + rangeExpr);
String l = matcher.group(1);
String h = matcher.group(2);
h = h.isEmpty() ? l : h;
weight = (short) (Math.max(l.length(), h.length()));
low = floor(new BigInteger(l));
high = ceiling(new BigInteger(h));
if (low.compareTo(high) > 0)
throw new IllegalArgumentException("Invalid range " + low + "/" + high);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BinRange binRange = (BinRange) o;
return weight == binRange.weight &&
Objects.equals(low, binRange.low) &&
Objects.equals(high, binRange.high);
}
@Override
public int hashCode() {
return Objects.hash(low, high, weight);
}
@Override
public String toString() {
return String.format("%02d:%s..%s [%s]", 19-weight, low, high, destination); // Warning, compareTo expects sorteable format
}
@Override
public int compareTo(Object o) {
return toString().compareTo(o.toString());
}
public boolean isInRange (BigInteger i) {
return i.compareTo(low) >=0 && i.compareTo(high) <=0;
}
public String destination() {
return destination;
}
static BigInteger floor(BigInteger i) {
if (!BigInteger.ZERO.equals(i)) {
int digits = (int)Math.log10(i.doubleValue())+1;
i = i.multiply(BigInteger.TEN.pow(19-digits));
}
return i;
}
private BigInteger ceiling (BigInteger i) {
int digits = BigInteger.ZERO.equals(i) ? 1 : (int)Math.log10(i.doubleValue())+1;
return floor(i).add(BigInteger.TEN.pow(19-digits)).subtract(BigInteger.ONE);
}
}
private static class PanRegExp {
private String destination;
private Pattern pattern;
PanRegExp(String destination, String regexp) {
this.destination = destination;
this.pattern = Pattern.compile(regexp);
}
public String destination() {
return destination;
}
public boolean matches(String pan) {
return pattern.matcher(pan).matches();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PanRegExp panRegExp = (PanRegExp) o;
return Objects.equals(destination, panRegExp.destination) &&
Objects.equals(pattern, panRegExp.pattern);
}
@Override
public int hashCode() {
return Objects.hash(destination, pattern);
}
@Override
public String toString() {
return String.format("%s [%s]", pattern.toString(), destination);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy