All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jpos.transaction.participant.SelectDestination Maven / Gradle / Ivy

Go to download

jPOS is an ISO-8583 based financial transaction library/framework that can be customized and extended in order to implement financial interchanges.

There is a newer version: 3.0.0
Show newest version
/*
 * 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