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

org.kapott.hbci.dialog.HBCIJobsDialog Maven / Gradle / Ivy

Go to download

HBCI4j - Home Banking Computer Interface for Java - Clone from https://github.com/hbci4j/hbci4java

There is a newer version: 3.5.46
Show newest version
/*
 * Copyright 2018-2019 adorsys GmbH & Co KG
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.kapott.hbci.dialog;

import lombok.extern.slf4j.Slf4j;
import org.kapott.hbci.GV.AbstractHBCIJob;
import org.kapott.hbci.GV.GVTAN2Step;
import org.kapott.hbci.exceptions.HBCI_Exception;
import org.kapott.hbci.manager.*;
import org.kapott.hbci.passport.PinTanPassport;
import org.kapott.hbci.protocol.Message;
import org.kapott.hbci.status.HBCIExecStatus;
import org.kapott.hbci.status.HBCIMsgStatus;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/* @brief A class for managing exactly one HBCI-Dialog

    A HBCI-Dialog consists of a number of HBCI-messages. These
    messages will be sent (and the responses received) one
    after the other, without timegaps between them (to avoid
    network timeout problems).

    The messages generated by a HBCI-Dialog are at first DialogInit-Message,
    after that a message that contains one ore more "Geschaeftsvorfaelle"
    (i.e. the stuff that you really want to do via HBCI), and at last
    a DialogEnd-Message.

    In this class we have two API-levels, a mid-level API (for manually
    creating and processing dialogs) and a high-level API (for automatic
    creation of typical HBCI-dialogs). For each method the API-level is
    given in its description
*/
@Slf4j
public final class HBCIJobsDialog extends AbstractHbciDialog {

    private HBCIMessageQueue queue = new HBCIMessageQueue();
    private HashMap listOfGVs = new HashMap<>();

    public HBCIJobsDialog(PinTanPassport passport) {
        this(passport, null, -1);
    }

    public HBCIJobsDialog(PinTanPassport passport, String dialogId, long msgnum) {
        super(passport);
        this.dialogId = dialogId;
        this.msgnum = msgnum;
    }

    public HBCIMessage addTask(AbstractHBCIJob job) {
        return this.addTask(job, true);
    }

    public HBCIMessage addTask(AbstractHBCIJob job, boolean verify) {
        try {
            log.info(HBCIUtils.getLocMsg("EXCMSG_ADDJOB", job.getName()));
            if (verify) {
                job.verifyConstraints();
            }

            // check bpd.numgva here
            String hbciCode = job.getHBCICode();
            if (hbciCode == null) {
                throw new HBCI_Exception(job.getName() + " not supported");
            }

            int gva_counter = listOfGVs.size();
            Integer counter_st = listOfGVs.get(hbciCode);
            int gv_counter = counter_st != null ? counter_st : 0;
            int total_counter = getTotalNumberOfGVSegsInCurrentMessage();

            gv_counter++;
            total_counter++;
            if (counter_st == null) {
                gva_counter++;
            }

            // BPD: max. Anzahl GV-Arten
            int maxGVA = passport.getMaxGVperMsg();
            // BPD: max. Anzahl von Job-Segmenten eines bestimmten Typs
            int maxGVSegJob = job.getMaxNumberPerMsg();
            // Passport: evtl. weitere Einschränkungen bzgl. der Max.-Anzahl
            // von Auftragssegmenten pro Nachricht
            int maxGVSegTotal = passport.getMaxGVSegsPerMsg();

            if ((maxGVA > 0 && gva_counter > maxGVA) ||
                (maxGVSegJob > 0 && gv_counter > maxGVSegJob) ||
                (maxGVSegTotal > 0 && total_counter > maxGVSegTotal)) {
                if (maxGVSegTotal > 0 && total_counter > maxGVSegTotal) {
                    log.debug(
                        "have to generate new message because current type of passport only allows " + maxGVSegTotal + " GV segs per message");
                } else {
                    log.debug(
                        "have to generate new message because of BPD restrictions for number of tasks per message; " +
                            "adding job to this new message");
                }
                newMsg();
                gv_counter = 1;
            }

            listOfGVs.put(hbciCode, gv_counter);
            HBCIMessage last = queue.getLast();
            last.append(job);
            return last;
        } catch (Exception e) {
            String msg = HBCIUtils.getLocMsg("EXCMSG_CANTADDJOB", job.getName());
            log.error("task " + job.getName() + " will not be executed in current dialog");
            throw new HBCI_Exception(msg, e);
        }
    }

    private int getTotalNumberOfGVSegsInCurrentMessage() {
        int total = 0;

        for (Map.Entry hbciCode : listOfGVs.entrySet()) {
            total += hbciCode.getValue();
        }

        log.debug("there are currently " + total + " GV segs in this message");
        return total;
    }

    private void newMsg() {
        log.debug("starting new message");
        this.queue.append(new HBCIMessage());
        listOfGVs.clear();
    }

    private List doJobs() {
        log.info(HBCIUtils.getLocMsg("LOG_PROCESSING_JOBS"));

        ArrayList messageStatusList = new ArrayList<>();

        while (true) {
            HBCIMessage msg = this.queue.poll();

            if (msg == null) {
                break;
            }
            patchMessageForSca(msg);
            boolean veu = msg.getTasks().stream().anyMatch(AbstractHBCIJob::isVeu);

            HBCIMsgStatus msgstatus = new HBCIMsgStatus();

            boolean addMsgStatus = true;

            try {
                int taskNum = 0;

                Message message = MessageFactory.createMessage(veu ? "CustomMsgVeu" : "CustomMsg", passport.getSyntaxDocument());

                // durch alle jobs loopen, die eigentlich in der aktuellen
                // nachricht abgearbeitet werden müssten
                for (AbstractHBCIJob task : msg.getTasks()) {
                    if (task.skipped())
                        continue;

                    // Uebernimmt den aktuellen loop-Wert in die Lowlevel-Parameter
                    task.applyOffset();
                    task.setIdx(taskNum);

                    // Daten für den Task festlegen
                    String header = HBCIUtils.withCounter("GV", taskNum);
                    task.getLowlevelParams().forEach((key, value) ->
                        message.rawSet(header + "." + key, value));

                    taskNum++;
                }

                // Das passiert immer dann, wenn wir in der Message nur ein HKTAN#2 aus Prozess-Variante 2 hatten.
                // Dieses aufgrund einer 3076-SCA-Ausnahme aber nicht benoetigt wird.
                if (taskNum == 0) {
                    addMsgStatus = false;
                    break;
                }

                message.rawSet("MsgHead.dialogid", dialogId);
                message.rawSet("MsgHead.msgnum", Long.toString(msgnum));
                message.rawSet("MsgTail.msgnum", Long.toString(msgnum));

                // nachrichtenaustausch durchführen
                msgstatus = kernel.rawDoIt(message, null, HBCIKernel.SIGNIT, HBCIKernel.CRYPTIT);
                nextMsgNum();

                final int segnum = msgstatus.findTaskSegment();
                if (segnum != 0) {
                    // für jeden Task die entsprechenden Rückgabedaten-Klassen füllen
                    for (AbstractHBCIJob task : msg.getTasks()) {
                        if (task.skipped())
                            continue;

                        try {
                            task.fillJobResult(msgstatus, segnum);
                        } catch (Exception e) {
                            msgstatus.addException(e);
                        }
                    }
                }

                if (msgstatus.hasExceptions()) {
                    log.error("aborting current loop because of errors");
                    break;
                }

                ////////////////////////////////////////////////////////////////////
                // Jobs erneut ausfuehren, falls noetig.
                HBCIMessage newMsg = null;
                for (AbstractHBCIJob task : msg.getTasks()) {
                    if (task.skipped())
                        continue;

                    AbstractHBCIJob redo = task.redo();
                    if (redo != null) {
                        // Nachricht bei Bedarf erstellen und an die Queue haengen
                        if (newMsg == null) {
                            newMsg = new HBCIMessage();
                            queue.append(newMsg);
                        }

                        // Task hinzufuegen
                        log.debug("repeat task " + redo.getName());
                        newMsg.append(redo);
                    }
                }
                //
                ////////////////////////////////////////////////////////////////////
            } catch (Exception e) {
                msgstatus.addException(e);
            } finally {
                if (addMsgStatus) {
                    messageStatusList.add(msgstatus);
                }
            }
        }

        return messageStatusList;
    }

    private void patchMessageForSca(HBCIMessage msg) {
        //patch offset jobs, append missing hktan task
        if (msg.findTask("HKTAN") == null) {
            msg.getTasks().stream()
                .filter(passport::tan2StepRequired)
                .findAny()
                .map(this::createHktan)
                .ifPresent(msg::append);
        }
    }

    private GVTAN2Step createHktan(AbstractHBCIJob tan2StepRequiredJob) {
        final GVTAN2Step hktan = new GVTAN2Step(getPassport(), tan2StepRequiredJob);
        hktan.setProcess(KnownTANProcess.PROCESS2_STEP1);
        hktan.setSegVersion(getPassport().getCurrentSecMechInfo().getSegversion());

        if (getPassport().tanMediaNeeded()) {
            hktan.setParam("tanmedia", getPassport().getCurrentSecMechInfo().getMedium());
        }

        return hktan;
    }

    /**
     * 

Ausführen aller bisher erzeugten Aufträge. Diese Methode veranlasst den HBCI-Kernel, * die Aufträge, die durch die Aufrufe auszuführen.

* * @param close * @return ein Status-Objekt, anhand dessen der Erfolg oder das Fehlschlagen * der Dialoge festgestellt werden kann. */ @Override public HBCIExecStatus execute(boolean close) { HBCIExecStatus ret; log.debug("executing dialog"); try { ret = new HBCIExecStatus(doJobs()); } catch (Exception e) { ret = new HBCIExecStatus(null); ret.addException(e); } if (close) { dialogEnd(); } return ret; } @Override public long getMsgnum() { return msgnum; } @Override public boolean isAnonymous() { return false; } private void nextMsgNum() { msgnum++; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy