/**********************************************************************
*
* Copyright (c) by Olaf Willuhn
* All rights reserved
*
**********************************************************************/
package de.willuhn.jameica.hbci.server.hbci;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.kapott.hbci.GV_Result.GVRDauerList;
import org.kapott.hbci.GV_Result.GVRDauerList.Dauer;
import de.willuhn.datasource.rmi.DBIterator;
import de.willuhn.jameica.hbci.HBCIProperties;
import de.willuhn.jameica.hbci.messaging.ImportMessage;
import de.willuhn.jameica.hbci.messaging.ObjectChangedMessage;
import de.willuhn.jameica.hbci.rmi.BaseDauerauftrag;
import de.willuhn.jameica.hbci.rmi.Konto;
import de.willuhn.jameica.hbci.rmi.Protokoll;
import de.willuhn.jameica.hbci.rmi.SepaDauerauftrag;
import de.willuhn.jameica.hbci.server.Converter;
import de.willuhn.jameica.system.Application;
import de.willuhn.logging.Logger;
import de.willuhn.util.ApplicationException;
/**
* Job fuer "SEPA-Dauerauftraege abrufen".
*/
public class HBCISepaDauerauftragListJob extends AbstractHBCIJob
{
private Konto konto = null;
/**
* @param konto Konto, ueber welches die existierenden SEPA-Dauerauftraege abgerufen werden.
* @throws ApplicationException
* @throws RemoteException
*/
public HBCISepaDauerauftragListJob(Konto konto) throws ApplicationException, RemoteException
{
try
{
if (konto == null)
throw new ApplicationException(i18n.tr("Bitte w�hlen Sie ein Konto aus"));
if (konto.isNewObject())
konto.store();
this.konto = konto;
org.kapott.hbci.structures.Konto own = Converter.HibiscusKonto2HBCIKonto(konto);
// Deutsche Umlaute im eigenen Namen noch ersetzen
// siehe http://www.onlinebanking-forum.de/phpBB2/viewtopic.php?t=16052
own.name = HBCIProperties.replace(own.name,HBCIProperties.TEXT_REPLACEMENTS_SEPA);
setJobParam("src",own);
}
catch (RemoteException e)
{
throw e;
}
catch (ApplicationException e2)
{
throw e2;
}
catch (Throwable t)
{
Logger.error("error while executing job " + getIdentifier(),t);
throw new ApplicationException(i18n.tr("Fehler beim Erstellen des Auftrags. Fehlermeldung: {0}",t.getMessage()),t);
}
}
/**
* @see de.willuhn.jameica.hbci.server.hbci.AbstractHBCIJob#getIdentifier()
*/
public String getIdentifier()
{
return "DauerSEPAList";
}
/**
* @see de.willuhn.jameica.hbci.server.hbci.AbstractHBCIJob#getName()
*/
public String getName() throws RemoteException
{
return i18n.tr("Abruf SEPA-Dauerauftr�ge {0}",konto.getLongName());
}
/**
* @see de.willuhn.jameica.hbci.server.hbci.AbstractHBCIJob#markExecuted()
*/
void markExecuted() throws RemoteException, ApplicationException
{
GVRDauerList result = (GVRDauerList) getJobResult();
// Abgleich mit den lokalen SEPA-Dauerauftraegen
DBIterator existing = konto.getSepaDauerauftraege();
GVRDauerList.Dauer[] lines = result.getEntries();
SepaDauerauftrag remote = null;
SepaDauerauftrag local = null;
// Hier drin merken wir uns alle SepaDauerauftraege, die beim Abgleich
// gefunden wurden. Denn die muessen garantiert nicht lokal geloescht werden.
Map<SepaDauerauftrag,Boolean> matches = new HashMap<SepaDauerauftrag,Boolean>();
////////////////////////////////////////////////////////////////////////////
// 1. Nach neuen und geaenderten Dauerauftraegen suchen
Logger.info("checking for new and changed entries");
for (int i=0;i<lines.length;++i)
{
try
{
remote = this.create(lines[i]);
local = find(existing,remote);
if (local != null)
{
Logger.info("found a local copy. order id: " + remote.getOrderID() + ". Checking for modifications");
matches.put(local,Boolean.TRUE);
if (remote.getChecksum() != local.getChecksum())
{
Logger.info("modifications found, updating local copy");
local.overwrite(remote);
this.store(local);
Application.getMessagingFactory().sendMessage(new ObjectChangedMessage(local));
}
}
else
{
Logger.info("no local copy found. adding sepa-dauerauftrag order id: " + remote.getOrderID());
this.store(remote);
Application.getMessagingFactory().sendMessage(new ImportMessage(remote));
}
}
catch (Exception e)
{
Logger.error("error while checking sepa-dauerauftrag, skipping this one",e);
}
}
//
////////////////////////////////////////////////////////////////////////////
Logger.info("checking for deletable entries");
existing.begin();
// Wir koennen jetzt hier alle loeschen, die NICHT in matches gefunden (also nicht von der Bank geliefert wurden)
// aber eine Order-ID haben und somit aktiv sein muessten
while (existing.hasNext())
{
local = (SepaDauerauftrag) existing.next();
if (!local.isActive())
{
Logger.info("skipping [id: " + local.getID() + "] - not yet submitted");
continue; // der wurde noch nicht zur Bank geschickt und muss daher auch nicht geloescht werden
}
if (matches.containsKey(local))
{
Logger.info("skipping [id: " + local.getID() + ", order id: " + local.getOrderID() + "] - just matched");
continue;
}
Logger.info("sepa-dauerauftrag order id: " + local.getOrderID() + " does no longer exist online, can be deleted");
local.delete();
}
Application.getMessagingFactory().sendMessage(new ObjectChangedMessage(konto));
konto.addToProtokoll(i18n.tr("SEPA-Dauerauftr�ge abgerufen"),Protokoll.TYP_SUCCESS);
Logger.info("sepa-dauerauftrag list fetched successfully");
}
/**
* Durchucht die Liste der lokalen SEPA-Dauerauftraege nach dem angegebenen.
* Insofern die Bank korrekte Order-IDs liefert, findet die Funktion auch
* dann die lokale Kopie, wenn Bankseitig Eigenschaften des SEPA-Dauerauftrages
* (z.Bsp. Betrag oder Turnus) geaendert wurden.
* @param existing Liste der lokal vorhandenen Dauerauftraege.
* @param remote von der Bank gelieferter Dauerauftrag.
* @return die lokale Kopie des Dauerauftrages oder NULL, wenn keine
* lokale Kopie vorhanden ist.
* @throws RemoteException
*/
private SepaDauerauftrag find(DBIterator existing, SepaDauerauftrag remote) throws RemoteException
{
existing.begin();
while (existing.hasNext())
{
SepaDauerauftrag local = (SepaDauerauftrag) existing.next();
if (!local.isActive())
continue; // der ist noch gar nicht bei der Bank und muss daher auch nicht abgeglichen werden
String idLocal = StringUtils.trimToEmpty(local.getOrderID());
String idRemote = StringUtils.trimToEmpty(remote.getOrderID());
// Platzhalter-ID verwenden, wenn die Bank keine uebertragen hat
if (idRemote.length() == 0)
idRemote = BaseDauerauftrag.ORDERID_PLACEHOLDER;
if (idLocal.equals(idRemote))
{
// OK, die IDs sind schonmal identisch. Jetzt noch checken, ob
// es wirklich eine echte ID ist oder der Platzhalter
if (!idLocal.equals(BaseDauerauftrag.ORDERID_PLACEHOLDER))
return local; // Echte ID - also haben wir ihn gefunden
// Es ist also die Platzhalter-ID. Dann vergleichen wir
// anhand der Eigenschaften
if (local.getChecksum() == remote.getChecksum())
return local; // sind identisch - gefunden
}
}
// Nicht gefunden
return null;
}
/**
* Erstellt ein lokales SepaDauerauftrags-Objekt aus dem Remote-Auftrag von HBCI4Java.
* @param remote der Remote-Auftrag von HBCI4Java.
* @return das lokale Objekt.
* @throws RemoteException
* @throws ApplicationException
*/
private SepaDauerauftrag create(Dauer remote) throws RemoteException, ApplicationException
{
SepaDauerauftrag auftrag = Converter.HBCIDauer2HibiscusSepaDauerauftrag(remote);
auftrag.setKonto(this.konto); // Konto hart zuweisen - sie kamen ja auch von dem
return auftrag;
}
/**
* Speichert den Dauerauftrag, faengt aber ggf. auftretende Exceptions und loggt sie.
* @param auftrag der zu speichernde Auftrag.
* BUGZILLA 1031
*/
private void store(SepaDauerauftrag auftrag)
{
try
{
auftrag.store();
}
catch (Exception e) // BUGZILLA 1031
{
try
{
Logger.error("unable to store dauerauftrag " + auftrag.getOrderID() + ", skipping",e);
this.konto.addToProtokoll(i18n.tr("Speichern des SEPA-Dauerauftrages fehlgeschlagen: {0}",e.getMessage()),Protokoll.TYP_ERROR);
}
catch (Exception e2)
{
Logger.error("unable to log error",e2); // useless
}
}
}
/**
* @see de.willuhn.jameica.hbci.server.hbci.AbstractHBCIJob#markFailed(java.lang.String)
*/
String markFailed(String error) throws RemoteException, ApplicationException
{
String msg = i18n.tr("Fehler beim Abrufen der SEPA-Dauerauftr�ge: {0}",error);
konto.addToProtokoll(msg,Protokoll.TYP_ERROR);
return msg;
}
}