Lesbare args für die main-Methode

  • Themenstarter Themenstarter Gelöschtes Mitglied 68249
  • Beginndatum Beginndatum
G

Gelöschtes Mitglied 68249

Gast
Hi,
ich habe folgendes Problem:
wir haben eine Reihe von Programmen, die die Datenqualität einer Datenbank verbessern sollen. Diese laufen täglich, oder wöchentlich oder monatlich. Da haben sich im Laufe der Jahre mehrere Programme angesammelt.
Jetzt war es so, dass es eine Batch-Datei zu jedem Programm gab und diese dann in der Windows Tasksliste zeitlich gesteuert ausgeführt worden ist. Natürlich Nachts um keine Performance zu klauen. Da die Tools unterschiedlich lange dauern und teilweise aber auf dieselben Daten zugreifen, wurden die dann im Task-Planer mit unterschiedlichen Startzeiten geplant. Mal mit einer Stunde Zeit für aufwändigere Sachen und mal mit einer halben Stunde Zeit für nicht so aufwendige.
Nach den ganzen Überarbeitungen werden die Daten dann zu allem Überfluss auch noch exportiert und an eine andere Abteilung gesendet, damit diese auch Dinge mit den Daten tut. Problem war, dass die Nacht langsam aber sicher zu kurz geworden ist für die ganzen Tools, damit am nächsten Morgen der Export auch noch pünktlich raus geht.
Also bin ich jetzt hergegangen und habe alle Tools unter ein großes Tool gestellt, welches zu einer Zeit startet und die Tools einfach sequentiell abarbeitet, das spart einen Haufen Zeit, weil ich mir keine Puffer mehr planen muss, weil das Kontroll-Tool weiß ja, wann eins der kleinen Programme fertig ist und startet dann direkt das nächste.

Nun zu meiner Frage:
die Kontrolle, welches Tool gestartet werden soll und welches nicht, mache ich aktuell über ein Array von 0 und 1, welches ich als Argument mit übergebe. So kann ich mit einer Kombination aus Nullen und Einsen sowohl ein Tool alleine, als auch eine Gruppe von Tools starten.
Das funktioniert zwar, aber nur so lange, wie ich lebe. Das System ist von außen nicht wirklich wartbar, bzw. man kann nicht direkt sehen, was eine 1 an welcher Stelle macht.
Aktuell sieht das so aus:
Java:
public static void main(String[] args) {

    try {
        boolean tool1 = getArg(args, 0);
        boolean tool2 = getArg(args, 1);
        boolean tool3 = getArg(args, 2);
        boolean tool4 = getArg(args, 3);

        LOG.info("Tool1 = " + tool1);
        LOG.info("Tool2 = " + tool2);
        LOG.info("Tool3 = " + tool3);
        LOG.info("Tool4 = " + tool4);
        LOG.info(" ");

        if(tool1) {
            try { tool1.main(null); } catch (Exception e) { LOG.error("Fehler in tool1: ", e); }
        }
        if(tool2) {
            try { tool2.main(null); } catch (Exception e) { LOG.error("Fehler in tool2: ", e); }
        }
    } catch (Exception e) {
        LOG.error("Fehler im Prüfer", e);
    }
}

private static boolean getArg(String[] args, int i) {
    if(args == null) return true;
    if(args.length == 0) return true;
    if((args.length - 1) < i) return false;

    String arg = args[i];

    return ("1".equals(arg));
}
Ich suche aber nach einer Möglichkeit, dass man das irgendwie "lesbar" konfigurieren könnte. Mir fällt aber nichts ein.
 
Du kannst doch den Tools einfach Namen geben und dann eben diese als Parameter verwenden. Also dann sind die Aufrufe halt mit den Parametern "tool1 tool3" und dann ist klar: tool1 und tool3 sollen gestartet werden.

Wie das implementiert werden kann, hängt dann von den ganz genauen Anforderungen ab. Wenn die Reihenfolge fest steht, in der die Aufrufe erfolgen sollen, dann könnte es einfach ein Set sein, das gefüllt wird. Wenn etwas kein gültiger Wert ist (Werte können übe reine enum definiert sein), dann kannst Du auf den falschen Parameter reagieren. Und bei der Ausführung prüfst Du dann: Ist in dem Set das Tools.TOOL1 enthalten? Ja, dann ausführen.
Und ohne Parameter wird alles ausgeführt? Das kann dann halt die erweitere Prüfung sein: "Ist Tools.TOOL1 im set enthalten oder ist das set leer?" (Oder Du erzeugst dann ein Set mit allen Elementen - dann kannst Du da leichter etwas anpassen)
 
Das klingt erstaunlich einfach mit den Enums.
Hatte Zwischenzeitlich schon über eine Config nachgedacht, in der ich Sets definiere für wöchentliche tägliche und spezielle Ausführungen und dann eben diese Listen ähnlich verwende.
Aber noch lebe ich und bin in der Firma, da kann ich mir mal einen Kopf machen.
 
Das System ist von außen nicht wirklich wartbar, bzw. man kann nicht direkt sehen, was eine 1 an welcher Stelle macht.
Bei den meisten Kommandozeilen-Tools werden Argumente so übergeben, dass es eine Option gibt, deren Name ein Minuszeichen oder Schrägstrich oder anderes als Vorzeichen hat.

Auf die Option folgt dann der eigentliche Wert.

Dafür gibt es jede Menge Java-Libs.

Google Suche: java command line library
 
Du koenntest auch beinhart die Klassennamen verwenden:

Code:
your-program.java firsttool secondtool

Java:
List<Class<?>> toolClasses = new ArrayList<>();

for (String arg : args) {
    toolClasses.add(Main.class.getClassLoader().loadClass("your.package.tools." + arg));
}

for (Class<?> toolClass : toolClasses) {
    System.out.println("Running <" + toolClass.getName() + ">...");
    toolClass.getMethod("main").invoke();
}

Ist etwas Pseudo-Code, aber das sollte dir eine grobe Idee geben. Der Nachteil ist man muss die Klassennamen kennen, aber die Parameter muss man so oder so wissen.

---

Fuer das Verwalten von Kommandozeilenargumenten ist picocli wirklich super, hat haufenweise Funktionalitaet.
 
Ich habe hier jetzt evtl. eine Meinung, mit der ich etwas alleine da stehe, aber ich finde, man bindet hier eine ganze Library ein für etwas, das eigentlich sehr einfach schnell selbst implementiert ist. Vor allem holt man sich eine Komplexität ins Haus, die man gar nicht braucht.

So habe ich (für deutlich komplexere Anforderungen) Mal zwei Klassen geschrieben, die Argumente parsen können. Die eine Klasse ist halt der ArgumentParser der das eigentliche Doing macht und die zweite Klasse ist der Parameter, der beschrieben wird. Der hat dann so Dinge wie:
  • der eigentliche Parameter (z.B. eben das "-x")
  • kurze Beschreibung ("-x <whatever> : Setzt den Parameter x")
  • lange Beschreibung ("-x macht was ganz tolles ... und ich beschreibe hier so viel Roman wie ich will ...")
  • Anzahl zusätzliche Werte (min / max)
  • Callback (Da wurden dann die zusätzlichen Werte mit übergeben)

Das liess sich dann auch sehr einfach nutzen und man hatte halt direkt auch die Usage über die kurze Beschreibung und die Möglichkeit eine lange Beschreibung anzufordern.

Die erste Version hatte das mit der Dokumentation nicht mit drin - aber war in sehr kurzer Zeit geschrieben. Aber selbst das ist ja hier schon overkill. Das kann ja schon einfach sein:
Java:
for (String arg : args) {
    switch(arg) {
        case "Test1" :
            doTest1();
            break;
            
        case "Test2" :
            doTest2();
            break;
            
        case "Test3" :
            doTest3();
            break;
            
        case "Test4" :
            doTest4();
            break;
            
        default:
            System.out.println("Argument " + arg + " unknown!");
    }
}
(So man damit leben kann, das die Reihenfolge und Anzahl der Aufrufe nicht festgelegt ist. Ein Aufruf mit Test3 Test1 Test1 würde halt Test3 zuerst aufrufen und dann Test1 zwei mal.)
 
Ich habe hier jetzt evtl. eine Meinung, mit der ich etwas alleine da stehe, aber ich finde, man bindet hier eine ganze Library ein für etwas, das eigentlich sehr einfach schnell selbst implementiert ist. Vor allem holt man sich eine Komplexität ins Haus, die man gar nicht braucht.
Ja, das ist richtig, aber ich wollte es mal erwaehnt haben wenn mir die Bibliothek gut gefaellt. Mit picocli bekommt man auch viele Sachen direkt dazu, zum Beispiel --help Ausgaben, man pages, Shell Autovervollstaendigungen und noch so biszchen was. Ob man das alles braucht oder nicht, ist eine andere Frage. Das, und viele moegen Annotations-basierte Loesungen.

Ich finde das Projekt schon interessant, aber fuer 'kleinere Sachen' schreibe ich auch immer das Zerlegen der Argumente selbst.
 

Zurück
Oben