Raščlanjivanje parametara naredbenog retka s JCommanderom

1. Pregled

U ovom vodiču, naučit ćemo kako koristiti JCommander za raščlanjivanje parametara naredbenog retka. Istražit ćemo nekoliko njegovih značajki dok gradimo jednostavnu aplikaciju naredbenog retka.

2. Zašto JCommander?

"Budući da je život prekratak za raščlanjivanje parametara naredbenog retka" - Cédric Beust

JCommander, koji je stvorio Cédric Beust, je knjižnica temeljena na bilješkama zaraščlanjivanje parametara naredbenog retka. Može smanjiti napor oko izgradnje aplikacija naredbenog retka i pomoći nam da im pružimo dobro korisničko iskustvo.

S JCommanderom možemo iskrcati nezgodne zadatke poput raščlanjivanja, provjere valjanosti i pretvorbe tipova, kako bismo se usredotočili na našu logiku aplikacije.

3. Postavljanje JCommandera

3.1. Maven konfiguracija

Počnimo dodavanjem jkomanda ovisnost u našem pom.xml:

 com.beust jcommander 1.78 

3.2. Pozdrav svijete

Stvorimo jednostavan HelloWorldApp koji uzima jedan ulaz koji se naziva Ime i ispisuje pozdrav, "Zdravo ".

Od JCommander veže argumente naredbenog retka na polja u Java klasi, prvo ćemo definirati a HelloWorldArgs razred s poljem Ime označen sa @Parametar:

klasa HelloWorldArgs {@Parameter (names = "--name", description = "User name", required = true) private String name; }

Sada, upotrijebimo JCommander klase za raščlanjivanje argumenata naredbenog retka i dodjeljivanje polja u našem HelloWorldArgs objekt:

HelloWorldArgs jArgs = novi HelloWorldArgs (); JCommander helloCmd = JCommander.newBuilder () .addObject (jArgs) .build (); helloCmd.parse (args); System.out.println ("Pozdrav" + jArgs.getName ());

Na kraju, pozovimo glavnu klasu s istim argumentima iz konzole:

$ java HelloWorldApp --ime JavaWorld Hello JavaWorld

4. Izgradnja stvarne aplikacije u JCommanderu

Sad kad smo već u pogonu, razmotrimo složeniji slučaj upotrebe - API klijent naredbenog retka koji komunicira s aplikacijom za naplatu kao što je Stripe, posebno s mjerenim (ili na temelju upotrebe) scenarijem naplate. Ova usluga naplate treće strane upravlja našim pretplatama i fakturiranjem.

Zamislimo da vodimo tvrtku SaaS, u kojoj naši kupci kupuju pretplate na naše usluge i naplaćuje im se broj API poziva našim uslugama mjesečno. Izvest ćemo dvije operacije s našim klijentom:

  • podnijeti: Pošaljite količinu i jediničnu cijenu upotrebe za kupca u odnosu na zadanu pretplatu
  • donijeti: Dohvaćanje naknada za kupca na temelju potrošnje na nekim ili svim njihovim pretplatama u tekućem mjesecu - možemo dobiti te troškove agregirane u sve pretplate ili razvrstane po svakoj pretplati

Izgradit ćemo API klijent dok prolazimo kroz značajke knjižnice.

Započnimo!

5. Definiranje parametra

Počnimo s definiranjem parametara koje naša aplikacija može koristiti.

5.1. The @Parametar Bilješka

Bilježenje polja s @Parametar govori JCommanderu da na njega veže odgovarajući argument naredbenog retka. @Parametar ima atribute koji opisuju glavni parametar, kao što su:

  • imena - jedno ili više imena opcije, na primjer “–name” ili “-n”
  • opis - značenje iza mogućnosti pomoći krajnjem korisniku
  • potreban - je li opcija obavezna, zadane vrijednosti lažno
  • arity - broj dodatnih parametara koje opcija troši

Konfigurirajmo parametar id kupca u našem scenariju mjerenja naplate:

@Parameter (names = {"--customer", "-C"}, description = "Id Kupca koji koristi usluge", arity = 1, obavezno = true) String customerId; 

Izvršimo sada našu naredbu s novim parametrom “–customer”:

$ java App --customer cust0000001A Pročitajte ID kupca: cust0000001A. 

Isto tako, možemo koristiti kraći parametar "-C" da bismo postigli isti učinak:

$ java App -C cust0000001A Pročitajte ID kupca: cust0000001A. 

5.2. Potrebni parametri

Tamo gdje je parametar obvezan, aplikacija izlazi izbacivanjem a ParameterException ako ga korisnik ne navede:

$ java Iznimka aplikacije u niti "main" com.beust.jcommander.ParameterException: Potrebna je sljedeća opcija: [--customer | -C]

Trebali bismo primijetiti da, općenito, bilo koja pogreška u raščlanjivanju parametara rezultira a ParameterException u JCommanderu.

6. Ugrađeni tipovi

6.1. IStringConverter Sučelje

JCommander izvodi pretvorbu tipa iz naredbenog retka Niz ulaz u tipove Java u našim razredima parametara. The IStringConverter sučelje obrađuje pretvorbu tipa parametra iz Niz bilo kojem proizvoljnom tipu. Dakle, svi ugrađeni pretvarači JCommandera implementiraju ovo sučelje.

JCommander dolazi s podrškom za uobičajene vrste podataka kao što su Niz, Cijeli broj, Booleova, BigDecimal, i Enum.

6.2. Vrste jednoaritijske

Arity se odnosi na broj dodatnih parametara koje opcija troši. JCommander's ugrađeni tipovi parametara imaju zadani aritet jedan, osim za Booleova i Popis. Stoga su uobičajeni tipovi poput Niz, Cijeli broj, BigDecimal, Dugo, i Enum, su jednoaritijski tipovi.

6.3. Booleova Tip

Polja tipa boolean ili Booleova ne trebaju nikakvi dodatni parametri - ove opcije imaju arity nula.

Pogledajmo primjer. Možda želimo naplatiti troškove za kupca, razvrstane prema pretplati. Možemo dodati a boolean polje detaljan, koji je lažno prema zadanim postavkama:

@Parameter (names = {"--itemized"}) privatni logički podatak; 

Naša bi aplikacija vratila agregirane troškove sa detaljan postavljen lažno. Kada pozivamo naredbeni redak s detaljni parametar, postavili smo polje na pravi:

$ java App - pod oznakom Read Oznaka s oznakama: true. 

To dobro funkcionira ako nemamo slučaj upotrebe u kojem uvijek želimo razvrstane troškove, ako nije drugačije određeno. Parametar bismo mogli promijeniti u notItemized, ali možda bi bilo jasnije moći pružiti lažno kao vrijednost detaljan.

Uvedimo ovo ponašanje pomoću zadane vrijednosti pravi za polje i postavljanje arity kao jedan:

@Parameter (names = {"--itemized"}, arity = 1) private boolean itemized = true; 

Sada, kada odredimo opciju, vrijednost će biti postavljena na lažno:

$ java App --itemized false Oznaka za čitanje stavljena u stavke: false. 

7. Popis Vrste

JCommander nudi nekoliko načina vezivanja argumenata za Popis polja.

7.1. Određivanje parametra više puta

Pretpostavimo da želimo naplatiti samo podskup korisničkih pretplata:

@Parameter (names = {"--subscription", "-S"}) privatni popis subscriptionIds; 

Polje nije obvezno, a aplikacija će naplatiti troškove za sve pretplate ako parametar nije naveden. Međutim, možemo odrediti više pretplata korištenjem imena parametra više puta:

$ java App -S pretplataA001 -S pretplataA002 -S pretplataA003 Pročitajte pretplate: [pretplataA001, pretplataA002, pretplataA003]. 

7.2. Uvez Popisi Korištenjem razdjelnika

Umjesto da više puta navedemo opciju, pokušajmo povezati popis dodavanjem zareza Niz:

$ java App -S pretplataA001, pretplataA002, pretplataA003 Pročitane pretplate: [pretplataA001, pretplataA002, pretplataA003]. 

Ovo koristi jednu vrijednost parametra (arity = 1) za predstavljanje popisa. JCommander će koristiti nastavu CommaParameterSplitter da vežu zarezom odvojene Niz našem Popis.

7.3. Uvez Popisi Korištenje prilagođenog razdjelnika

Zadani razdjelnik možemo nadjačati primjenom IParameterSplitter sučelje:

klasa ColonParameterSplitter implementira IParameterSplitter {@Override public List split (String value) {return asList (value.split (":")); }}

A zatim mapiranje provedbe na cjepidlaka atribut u @Parametar:

@Parameter (names = {"--subscription", "-S"}, splitter = ColonParameterSplitter.class) privatni popis subscriptionIds; 

Isprobajmo:

$ java App -S "pretplataA001: pretplataA002: pretplataA003" Pročitajte pretplate: [pretplataA001, pretplataA002, pretplataA003]. 

7.4. Promjenjiva Arity Popisi

Varijabilnost arititeta omogućuje nam izjavupopisi koji mogu uzimati neodređene parametre, do sljedeće opcije. Možemo postaviti atribut varijabilnostArity kao pravi kako bi se specificiralo ovo ponašanje.

Pokušajmo ovo za raščlanjivanje pretplata:

@Parameter (names = {"--subscription", "-S"}, variableArity = true) private List subscriptionIds; 

A kad pokrenemo našu naredbu:

$ java App -S pretplataA001 pretplataA002 pretplataA003 - slične čitane pretplate: [pretplataA001, pretplataA002, pretplataA003]. 

JCommander veže sve ulazne argumente nakon opcije "-S" na polje popisa, do sljedeće opcije ili kraja naredbe.

7.5. Fiksni Arity Popisi

Do sada smo vidjeli neograničene popise, gdje možemo proslijediti onoliko stavki popisa koliko želimo. Ponekad bismo možda htjeli ograničiti broj stavki prosljeđenih na Popis polje. Da bismo to učinili, možemo navedite cijelu vrijednost arityja za a Popis poljeda bude ograničeno:

@Parameter (names = {"--subscription", "-S"}, arity = 2) private List subscriptionIds; 

Fiksni arity prisiljava provjeru broja parametara prosljeđenih a Popis opcija i baca a ParameterException u slučaju kršenja:

$ java App -S pretplataA001 pretplataA002 pretplataA003 Proslijeđen je glavni parametar 'subscriptionA003', ali u vašoj arg klasi nije definiran glavni parametar 

Poruka pogreške sugerira da je, budući da je JCommander očekivao samo dva argumenta, pokušao raščlaniti dodatni ulazni parametar "subscriptionA003" kao sljedeću opciju.

8. Prilagođene vrste

Parametre također možemo vezati pisanjem prilagođenih pretvarača. Poput ugrađenih pretvarača, i prilagođeni pretvarači moraju implementirati IStringConverter sučelje.

Napišimo pretvarač za raščlanjivanje ISO8601 vremenske oznake:

klasa ISO8601TimestampConverter provodi IStringConverter {privatni statički konačni DateTimeFormatter TS_FORMATTER = DateTimeFormatter.ofPattern ("uuuu-MM-dd'T'HH: mm: ss"); @Override public Instant convert (String value) {try {return LocalDateTime .parse (value, TS_FORMATTER) .atOffset (ZoneOffset.UTC) .toInstant (); } catch (DateTimeParseException e) {throw new ParameterException ("Nevaljana vremenska oznaka"); }}} 

Ovaj će kôd raščlaniti ulaz Niz i vratite an Trenutak, bacajući a ParameterException ako postoji pogreška pretvorbe. Ovaj pretvarač možemo koristiti tako da ga vežemo za polje tipa Trenutak koristiti konverter atribut u @Parametar:

@Parameter (names = {"--timestamp"}, pretvarač = ISO8601TimestampConverter.class) privatni Trenutni vremenski žig; 

Pogledajmo na djelu:

$ java App - vremenska oznaka 2019-10-03T10: 58: 00 Pročitana vremenska oznaka: 2019-10-03T10: 58: 00Z.

9. Provjera valjanosti parametara

JCommander nudi nekoliko zadanih provjera valjanosti:

  • jesu li navedeni potrebni parametri
  • ako se broj navedenih parametara podudara s aritetom polja
  • da li svaki Niz parametar se može pretvoriti u odgovarajući tip polja

U Dodatku, možda bismo željeli dodati prilagođene provjere valjanosti. Na primjer, pretpostavimo da ID-ovi kupaca moraju biti UUID-ovi.

Možemo napisati validator za polje kupca koje implementira sučelje IParameterValidator:

klasa UUIDValidator implementira IParameterValidator {private static final String UUID_REGEX = "[0-9a-fA-F] {8} (- [0-9a-fA-F] {4}) {3} - [0-9a-fA- Ž] {12} "; @Override public void validate (ime niza, vrijednost niza) baca ParameterException {if (! IsValidUUID (value)) {throw new ParameterException ("Parametar niza" + vrijednost + "nije važeći UUID."); }} privatni logički isValidUUID (vrijednost niza) {return Pattern.compile (UUID_REGEX) .matcher (vrijednost) .matches (); }} 

Zatim ga možemo spojiti s validateWith atribut parametra:

@Parameter (names = {"--customer", "-C"}, validateWith = UUIDValidator.class) private String customerId; 

Ako naredbu pozovemo s korisničkim ID-om koji nije UUID, aplikacija izlazi s porukom o neuspjehu provjere valjanosti:

$ java aplikacija - C customer001 Parametar niza customer001 nije važeći UUID. 

10. Podnaredbe

Sad kad smo naučili o vezivanju parametara, spojimo sve kako bismo izgradili naše naredbe.

U JCommanderu možemo podržavati više naredbi, koje se nazivaju podnaredbama, svaka s različitim skupom opcija.

10.1. @Parameters Bilješka

Možemo koristiti @Parameters za definiranje pod naredbi. @Parameters sadrži atribut commandNames za prepoznavanje naredbe.

Ajmo modelirati podnijeti i donijeti kao pod naredbe:

@Parameters (commandNames = {"submit"}, commandDescription = "Predaj upotrebu za određenog kupca i pretplatu," + "prihvaća jednu stavku upotrebe") klasa SubmitUsageCommand {// ...} @Parameters (commandNames = {"fetch" }, commandDescription = "Dohvati troškove za kupca u tekućem mjesecu," + "može se razvrstati ili objediniti") klasa FetchCurrentChargesCommand {// ...} 

JCommander koristi atribute u @Parameters za konfiguriranje pod naredbi, kao što su:

  • commandNames - naziv podnaredbe; veže argumente naredbenog retka na klasu označenu s @Parameters
  • commandDescription - dokumentira svrhu podnaredbe

10.2. Dodavanje podnaredbi u JCommander

Dodamo naredbe u JCommander s addCommand metoda:

SubmitUsageCommand submitUsageCmd = novo SubmitUsageCommand (); FetchCurrentChargesCommand fetchChargesCmd = novo FetchCurrentChargesCommand (); JCommander jc = JCommander.newBuilder () .addCommand (submitUsageCmd) .addCommand (fetchChargesCmd) .build (); 

The addCommand metoda registrira podnaredbe s odgovarajućim imenima kako je navedeno u commandNames atribut @Parameters bilješka.

10.3. Raščlanjivanje naredbi

Da bismo pristupili korisnikovom izboru naredbe, prvo moramo raščlaniti argumente:

jc.parse (argumenti); 

Dalje, pod-naredbu možemo izdvojiti pomoću getParsedCommand:

Niz parsedCmdStr = jc.getParsedCommand (); 

Osim identificiranja naredbe, JCommander veže ostatak parametara naredbenog retka na njihova polja u podnaredbi. Sada samo moramo pozvati naredbu koju želimo koristiti:

prekidač (parsedCmdStr) {slučaj "submit": submitUsageCmd.submit (); pauza; slučaj "dohvaćanje": fetchChargesCmd.fetch (); pauza; zadano: System.err.println ("Neispravna naredba:" + parsedCmdStr); } 

11. Pomoć za upotrebu JCommandera

Možemo se zazivati upotreba za prikaz vodiča za uporabu. Ovo je sažetak svih opcija koje naša aplikacija koristi. U našoj se aplikaciji možemo pozivati ​​na upotrebu na glavnoj naredbi, ili alternativno, na svakoj od dvije naredbe "pošalji" i "dohvati" zasebno.

Prikaz upotrebe može nam pomoći na nekoliko načina: prikaz opcija pomoći i tijekom rukovanja pogreškama.

11.1. Prikazivanje mogućnosti pomoći

Opciju pomoći možemo povezati u naše naredbe pomoću a boolean parametar zajedno s atributom Pomozite postavljen pravi:

@Parameter (names = "--help", help = true) privatna logička pomoć; 

Zatim možemo otkriti je li u argumentima proslijeđen “–help” i pozvati upotreba:

ako (cmd.help) {jc.usage (); } 

Pogledajmo izlaznu pomoć za našu naredbu "submit":

$ java App submit --help Upotreba: submit [options] Opcije: * --customer, -C Id Kupca koji koristi usluge * - pretplata, -S Id Pretplate koja je kupljena * - količina Korištena količina ; prijavljena količina dodaje se tijekom obračunskog razdoblja * - vrsta cijene, -P Vrsta cijene prijavljene upotrebe (vrijednosti: [PRE_RATED, UNRATED]) * --timestamp Vremenska oznaka događaja upotrebe, mora biti u trenutnom obračunskom razdoblju - -cjena Ako je PRE_RATED, primjenjuje se jedinična cijena po prijavljenoj jedinici korištene količine 

The upotreba metoda koristi @Parametar atributi kao što su opis za prikaz korisnog sažetka. Parametri označeni zvjezdicom (*) obvezni su.

11.2. Rukovanje pogreškama

Možemo uhvatiti ParameterException i nazovite upotreba kako bi pomogao korisniku da shvati zašto njihov unos nije točan. ParameterException sadrži JCommander primjer za prikaz pomoći:

probajte {jc.parse (args); } catch (ParameterException e) {System.err.println (e.getLocalizedMessage ()); jc.usage (); } 

12. Zaključak

U ovom uputstvu koristili smo JCommander za izgradnju aplikacije naredbenog retka. Iako smo pokrili mnoge glavne značajke, u službenoj dokumentaciji ima još toga.

Kao i obično, izvorni kod za sve primjere dostupan je na GitHubu.