/* $Id: TestServer.java,v 1.20 2005/06/10 18:03:03 kleiner Exp $ This file is part of HBCI4Java Copyright (C) 2001-2005 Stefan Palme HBCI4Java is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. HBCI4Java 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.kapott.demo.hbci.server; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.InputStreamReader; import java.io.PrintWriter; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Enumeration; import java.util.Hashtable; import org.kapott.demo.hbci.server.backend.MyBackend; import org.kapott.hbci.datatypes.factory.SyntaxDEFactory; import org.kapott.hbci.exceptions.HBCI_Exception; import org.kapott.hbci.manager.HBCIUtils; import org.kapott.hbci.manager.HBCIUtilsInternal; import org.kapott.hbci.protocol.factory.DEFactory; import org.kapott.hbci.protocol.factory.DEGFactory; import org.kapott.hbci.protocol.factory.MSGFactory; import org.kapott.hbci.protocol.factory.MultipleDEGsFactory; import org.kapott.hbci.protocol.factory.MultipleDEsFactory; import org.kapott.hbci.protocol.factory.MultipleSEGsFactory; import org.kapott.hbci.protocol.factory.MultipleSFsFactory; import org.kapott.hbci.protocol.factory.SEGFactory; import org.kapott.hbci.protocol.factory.SFFactory; import org.kapott.hbci.security.factory.CryptFactory; import org.kapott.hbci.security.factory.SigFactory; import org.kapott.hbci.server.DialogMgr; import org.kapott.hbci.server.HBCIServer; import org.kapott.hbci.server.JobContext; import org.kapott.hbci.server.ServerCallback; import org.kapott.hbci.server.ServerData; import org.kapott.hbci.server.StatusProtEntry; import org.kapott.hbci.server.datastore.DataStore; import org.kapott.hbci.structures.Konto; import org.kapott.hbci.structures.Value; public class TestServer implements ServerCallback { public DataStore dataStore; private MyBackend backend; private HBCIServer server; private ServerAdmin serverAdmin; private static final boolean INVERSE=true; private static final boolean TERMINATED=true; public static void main(String[] args) throws Exception { // erstes argument ist verzeichnisname fr server-daten boolean noConsole=args.length>=2 && args[1].equals("noconsole"); new TestServer().start(args[0],!noConsole); } public void start(String directory,boolean useConsole) { // hbci-server mit neuem datastore initialisieren dataStore=new MyDataStore(directory); backend=new MyBackend(directory+"-backend"); server=new HBCIServer(dataStore,this); initRMI(); // zustzlichen thread fr "admin-console" starten if (useConsole) { new Thread() { public void run() { System.out.println("starting admin console"); String command=null; while (true) { // "kommando" einlesen try { command=new BufferedReader(new InputStreamReader(System.in)).readLine(); } catch (Exception e) { System.out.println("problem with STDIN"); e.printStackTrace(); return; } try { if (command!=null) { if (command.equals("halt")) { // server anhalten server.stop(); closeRMI(); break; } else if (command.equals("reload")) { // datastore neu einlesen server.reInitializeServerData(); /* *** } else if (command.startsWith("iniletter")) { // iniletter fr benutzer ausgeben showINILetter(command.substring("iniletter".length())); */ } else if (command.equals("factorystats")) { // iniletter fr benutzer ausgeben showFactoryStats(); } else if (command.equals("cacheinfo")) { showCacheInfo(); } else if (command.length()!=0) { System.out.println(); System.out.println("unknown command!"); System.out.println("halt iniletter <user>"); System.out.println("reload factorystats"); System.out.println("cacheinfo"); System.out.println(); } } } catch (Exception e) { e.printStackTrace(); } } }}.start(); } // server starten server.start(); } // RMI server und objekte initialisieren private void initRMI() { try { Registry reg=null; try { reg=LocateRegistry.createRegistry(Registry.REGISTRY_PORT); HBCIUtils.log("starting new registry",HBCIUtils.LOG_DEBUG); } catch (Exception e) { reg=LocateRegistry.getRegistry(Registry.REGISTRY_PORT); HBCIUtils.log("using already running registry",HBCIUtils.LOG_DEBUG); } serverAdmin=new ServerAdminImpl(server,(MyDataStore)dataStore,backend); reg.rebind("serverAdmin",serverAdmin); } catch (Exception e) { throw new RuntimeException(e.getMessage(),e); } } // RMI objekte wieder aufrumen private void closeRMI() { try { Registry reg=LocateRegistry.getRegistry(Registry.REGISTRY_PORT); reg.unbind("serverAdmin"); } catch (Exception e) { throw new RuntimeException(e.getMessage(),e); } } // anzeigen eines INI briefes /* *** private void showINILetter(String who) { HBCIPassport passport=server.getPassport(who.trim()); INILetter iniletter=new INILetter(passport,INILetter.TYPE_INST); System.out.println(iniletter.toString()); } */ private void showFactoryStats() { System.out.println(); System.out.println("msg: "+MSGFactory.getInstance()); System.out.println("multisf: "+MultipleSFsFactory.getInstance()); System.out.println("sf: "+SFFactory.getInstance()); System.out.println("multiseg: "+MultipleSEGsFactory.getInstance()); System.out.println("seg: "+SEGFactory.getInstance()); System.out.println("multideg: "+MultipleDEGsFactory.getInstance()); System.out.println("deg: "+DEGFactory.getInstance()); System.out.println("multide: "+MultipleDEsFactory.getInstance()); System.out.println("de: "+DEFactory.getInstance()); System.out.println("sig: "+SigFactory.getInstance()); System.out.println("crypt: "+CryptFactory.getInstance()); System.out.println("syntax: "+SyntaxDEFactory.getInstance()); System.out.println(); } public void showCacheInfo() { System.out.println(); System.out.println("current dialogids:"); for (Enumeration e=DialogMgr.getInstance().getDialogs().keys();e.hasMoreElements();) { System.out.println(e.nextElement()); } System.out.println(); System.out.println("currently user entries:"); Hashtable userdata=ServerData.getInstance().getUserData(); for (Enumeration e=userdata.keys();e.hasMoreElements();) { String userid=(String)e.nextElement(); System.out.println(userid+":"+ ((((Hashtable)userdata.get(userid)).size()==0)?"not loaded":"loaded")); } System.out.println(); } // log-ausgaben des servers entgegennehmen public void log(String msg,int level,Date date,StackTraceElement trace) { String[] levels={"NON","ERR","WRN","INF","DBG","DB2"}; StringBuffer ret=new StringBuffer(128); ret.append("<").append(levels[level]).append("> "); SimpleDateFormat df=new SimpleDateFormat("yyyy.MM.dd HH:mm:ss.SSS"); ret.append("[").append(df.format(date)).append("] "); Thread thread=Thread.currentThread(); ret.append("[").append(thread.getThreadGroup().getName()+"/"+thread.getName()+"] "); String classname=trace.getClassName(); String hbciname="org.kapott.hbci."; if (classname!=null && classname.startsWith(hbciname)) ret.append(classname.substring((hbciname).length())).append(": "); if (msg!=null) ret.append(msg); // ausgabestream fr server-logs erzeugen PrintWriter logStream=null; try { logStream=new PrintWriter(new BufferedWriter(new FileWriter("server_"+HBCIUtils.getParam("connection.id")+".log",true))); } catch (Exception e) { throw new RuntimeException(e); } logStream.println(ret.toString()); logStream.close(); } // wird aufgerufen, wenn ein job eingegangen ist public void handleGV(JobContext context) { String jobname=context.getJobName(); System.out.println("have to handle job "+jobname+ " for userid/customer "+ context.getUserId()+"/"+context.getCustomerId()); if (jobname.equals("Saldo")) handleSaldoRequest(context); else if (jobname.equals("CustomMsg")) handleCustomMsg(context); else if (jobname.equals("Ueb")) handleUeb(context,!TERMINATED); else if (jobname.equals("TermUeb")) handleUeb(context,TERMINATED); else if (jobname.equals("Umb")) handleUmb(context); else if (jobname.equals("Last")) handleUeb(context,INVERSE,"71",!TERMINATED); else if (jobname.equals("KUmsZeit")) handleKUmsZeit(context); else if (jobname.equals("KUmsNew")) handleKUmsNew(context); else if (jobname.equals("Status")) handleStatusProt(context); else if (jobname.equals("SammelUeb")) handleMultiTransfer(context,!INVERSE); else if (jobname.equals("SammelLast")) handleMultiTransfer(context,INVERSE); else throw new HBCI_Exception("'"+jobname+"' not yet supported"); } // saldoabfrage handeln private void handleSaldoRequest(JobContext context) { // daten aus saldo-request extrahieren boolean allAccounts=context.getJobData("allaccounts").equals("J"); // abgefrages konto berprfen Konto acc=context.checkKTV("KTV"); if (acc==null) { return; } // *** maxentries/offset prfen // kontosammlung fr antwortdaten zusammenstellen Konto[] accounts; if (!allAccounts) accounts=new Konto[] {acc}; else accounts=context.getAllMyAccounts(); int counter=0; // alle kundenkonten durchlaufen for (int i=0;i<accounts.length;i++) { acc=accounts[i]; System.out.println("adding saldo for account "+acc.toString()); // kontosaldo in antwort einstellen (antwortdaten erzeugen) double saldo=backend.getSaldo(acc); context.setData(counter,"KTV.KIK.country",acc.country); context.setData(counter,"KTV.KIK.blz",acc.blz); context.setData(counter,"KTV.number",acc.number); context.setData(counter,"kontobez",acc.type); context.setData(counter,"curr",acc.curr); context.setData(counter,"booked.CreditDebit",saldo>=0?"C":"D"); context.setData(counter,"booked.value",HBCIUtils.value2String(Math.abs(saldo))); context.setData(counter,"booked.curr","EUR"); Date now=new Date(); context.setData(counter,"booked.date",HBCIUtils.date2String(now)); context.setData(counter,"booked.time",HBCIUtils.time2String(now)); context.setData(counter,"Timestamp.date",HBCIUtils.date2String(now)); context.setData(counter,"Timestamp.time",HBCIUtils.time2String(now)); context.addStatus(null,"0020","Daten zurckgemeldet",null); counter++; } } // eingehende kundenmeldung handeln public void handleCustomMsg(JobContext context) { // bergebene kontoverbindung berprfen Konto acc=context.checkKTV("KTV"); if (acc==null) { return; } System.out.println("Nachricht von Kunde "+context.getCustomerId()+" (Nutzerkennung "+context.getUserId()+"):"); System.out.println(" Konto: "+acc); System.out.println(" An: "+context.getJobData("recpt")); System.out.println(" Betreff: "+context.getJobData("betreff")); System.out.println(" Nachricht: "+context.getJobData("msg")); backend.storeCustomMsg(acc, context.getJobData("recpt"), context.getJobData("betreff"), context.getJobData("msg")); context.addStatus(null,"0010","Nachricht entgegengenommen",null); } // umbuchung mit zustzlichem test, ansonsten wie ueberweisung private void handleUmb(JobContext context) { Konto other=context.checkKTV("Other"); if (other==null) { return; } handleUeb(context,!TERMINATED); } private void handleUeb(JobContext context,boolean terminated) { handleUeb(context,false,"20",terminated); } // eingehenden berweisungsauftrag handeln private void handleUeb(JobContext context,boolean inverse,String myKey,boolean terminated) { Konto my=context.checkKTV("My"); Konto other=context.extractOtherAccount("Other"); String name=context.getJobData("name"); String name2=context.getJobData("name2"); Value btg=context.extractBTG("BTG"); String key=context.getJobData("key"); String addkey=context.getJobData("addkey"); String[] usage=context.extractStringArray("usage.usage"); Date date=context.extractDate("date"); String id=context.getJobData("id"); if (my==null) { return; } if (!terminated && date!=null) { context.addStatus("date","9150","Belegung nicht erlaubt",null); return; } if (terminated && date==null) { context.addStatus("date","9160","Ausfhrungsdatum fehlt",null); return; } // TODO minpretime und maxpretime testen if (id!=null) { context.addStatus("id","9150","Belegung nicht erlaubt",null); return; } // *** hoehe von btg ueberpruefen if (btg.value<=0) { context.addStatus("BTG.value","9215","Inhalt zu klein",null); return; } // waehrung von btg ueberprfen if (!btg.curr.equals("EUR")) { context.addStatus("BTG.curr","9210","Ungltige Whrung",null); return; } // *** key auf erlaubte werte berprfen // *** usage auf maxanzahl ueberpruefen other.name=name; other.name2=name2; if (terminated) System.out.print("terminated ("+HBCIUtils.date2String(date)+") "); System.out.println("request for "+(inverse?"debit note":"transfer")); System.out.println(" my account: "+my); System.out.println(" other account: "+other); System.out.println(" value: "+btg); if (!terminated) { /* btg wird immer zum my-konto addiert bei berweisungen muss dieser wert also negativ sein (weil das eigene konto ja belastet wird), bei lastschriften (inverse=true) muss der wert positiv bleiben, weil das eigene konto erhht wird */ if (!inverse) btg.value=-btg.value; backend.addTransfer(my,other,btg,usage,myKey,null); // hier alle konten bei dieser bank checken if ((other=context.extractOtherAccount("Other")).customerid!=null) { btg.value=-btg.value; backend.addTransfer(other,my,btg,usage,key,addkey); } context.addStatus(null,"0020","Auftrag ausgefhrt",null); } else { // TODO Auftrag muss in scheduled queue aufgenommen werden context.addStatus(null,"0010","Auftrag entgegengenommen",null); } } private void handleKUmsNew(JobContext context) { handleKUms(context,true); } // abfrage von kontoauszgen behandeln private void handleKUmsZeit(JobContext context) { handleKUms(context,false); } // abfrage von kontoauszgen behandeln private void handleKUms(JobContext context,boolean onlyNew) { Konto my=context.checkKTV("KTV"); Date from=null; Date to=null; if (!onlyNew) { String st; from=(st=context.getJobData("startdate"))!=null?HBCIUtils.string2Date(st):null; to=(st=context.getJobData("enddate"))!=null?HBCIUtils.string2Date(st):null; if (to!=null) { Calendar cal=Calendar.getInstance(); cal.setTime(to); cal.set(Calendar.HOUR,23); cal.set(Calendar.MINUTE,59); cal.set(Calendar.SECOND,59); to=cal.getTime(); } } String st=context.getJobData("allaccounts"); boolean allAccounts=(st!=null && st.equals("J")); if (my==null) { return; } // *** canallaccounts prfen // *** zeitangaben prfen // *** timerange prfen? // *** maxentries prfen // *** offset prfen // kontosammlung fr antwortdaten zusammenstellen Konto[] accounts; if (!allAccounts) accounts=new Konto[] {my}; else accounts=context.getAllMyAccounts(); // alle kundenkonten durchlaufen int counter=0; for (int i=0;i<accounts.length;i++) { Konto acc=accounts[i]; System.out.println("adding statement of account for "+acc.toString()); String data=backend.getStatementOfAccount(acc,from,to,onlyNew); if (data.length()!=0) { // daten in antwort setzen context.setData(counter,"booked","B"+data); context.addStatus(null,"0020","Daten zurckgemeldet",null); counter++; } else { context.addStatus(null,"3010","Keine Eintrge verfgbar",null); } } } private void handleStatusProt(JobContext context) { // TODO maxentries und offset auswerten Date startdate=context.extractDate("startdate"); Date enddate=context.extractDate("enddate"); if (startdate!=null && enddate!=null && startdate.compareTo(enddate)>0) { context.addStatus("enddate","9210","Endedatum liegt vor Startdatum",null); } else { System.out.println("returning status protocol for timerange '"+ ((startdate!=null)?HBCIUtils.date2String(startdate):"any")+ "' to '"+ ((enddate!=null)?HBCIUtils.date2String(enddate):"any")+ "'"); StatusProtEntry[] entries=((MyDataStore)dataStore).getStatusProt( context.getUserId(), startdate,enddate); for (int i=0;i<entries.length;i++) { StatusProtEntry entry=entries[i]; context.setData(i,"MsgRef.dialogid",entry.dialogid); context.setData(i,"MsgRef.msgnum",entry.msgnum); if (entry.segref!=null) { context.setData(i,"segref",entry.segref); } context.setData(i,"date",HBCIUtils.date2String(entry.timestamp)); context.setData(i,"time",HBCIUtils.time2String(entry.timestamp)); context.setData(i,"RetVal.code",entry.retval.code); if (entry.retval.deref!=null) { context.setData(i,"RetVal.ref",entry.retval.deref); } context.setData(i,"RetVal.text",entry.retval.text); // TODO param auch zurckgeben } if (entries.length!=0) { context.addStatus(null,"0020","Daten zurckgemeldet",null); } else { context.addStatus(null,"3010","Keine Eintrge verfgbar",null); } } } private void handleMultiTransfer(JobContext context,boolean inverse) { // my-KTV checken, wenn vorhanden System.out.println("processing mass "+(inverse?"debit":"transfer")); String data=context.getJobData("data"); // TODO dtaus-data auswerten context.addStatus(null,"0010","Auftrag entgegengenommen",null); } }