Analyse av kommandolinjeparametere med JCommander

1. Oversikt

I denne veiledningen, vi lærer hvordan vi bruker JCommander til å analysere kommandolinjeparametere. Vi vil utforske flere av funksjonene når vi bygger et enkelt kommandolinjeprogram.

2. Hvorfor JCommander?

"Fordi livet er for kort til å analysere kommandolinjeparametere" - Cédric Beust

JCommander, opprettet av Cédric Beust, er en kommentarbasert bibliotek foranalysere kommandolinjeparametere. Det kan redusere arbeidet med å bygge kommandolinjeapplikasjoner og hjelpe oss å gi dem en god brukeropplevelse.

Med JCommander kan vi laste av vanskelige oppgaver som parsing, validering og typekonvertering, slik at vi kan fokusere på applikasjonslogikken vår.

3. Sette opp JCommander

3.1. Maven-konfigurasjon

La oss begynne med å legge til jcommander avhengighet i vår pom.xml:

 com.beust jcommander 1.78 

3.2. Hei Verden

La oss lage en enkel HelloWorldApp som tar en enkelt inngang kalt Navn og skriver ut en hilsen, "Hallo ".

Siden JCommander binder kommandolinjeargumenter til felt i en Java-klasse, vil vi først definere en HelloWorldArgs klasse med felt Navn kommentert med @Parameter:

klasse HelloWorldArgs {@Parameter (names = "--name", beskrivelse = "Brukernavn", obligatorisk = true) privat strengnavn; }

La oss nå bruke JCommander klasse for å analysere kommandolinjeargumentene og tildele feltene i vår HelloWorldArgs gjenstand:

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

Til slutt, la oss påkalle hovedklassen med de samme argumentene fra konsollen:

$ java HelloWorldApp --navn JavaWorld Hello JavaWorld

4. Å bygge en reell applikasjon i JCommander

Nå som vi er i gang, la oss vurdere en mer kompleks brukstilfelle - en kommandolinjea API-klient som samhandler med et faktureringsprogram som Stripe, spesielt det målte (eller bruksbaserte) faktureringsscenariet. Denne tredjepartsfaktureringstjenesten administrerer abonnementene og faktureringene våre.

La oss forestille oss at vi driver en SaaS-virksomhet der kundene våre kjøper abonnement på tjenestene våre og faktureres for antall API-samtaler til tjenestene våre per måned. Vi utfører to operasjoner i vår klient:

  • sende inn: Send inn antall og enhetspris for bruk for en kunde mot et gitt abonnement
  • hente: Hent gebyrer for en kunde basert på forbruket på noen eller alle abonnementene i den aktuelle måneden - vi kan få disse kostnadene samlet over alle abonnementene eller spesifisert etter hvert abonnement

Vi bygger API-klienten når vi går gjennom bibliotekets funksjoner.

La oss begynne!

5. Definere en parameter

La oss begynne med å definere parametrene som applikasjonen vår kan bruke.

5.1. De @Parameter Kommentar

Kommenterer et felt med @Parameter forteller JCommander å binde et matchende kommandolinjeargument til det. @Parameter har attributter for å beskrive hovedparameteren, for eksempel:

  • navn - ett eller flere navn på alternativet, for eksempel “–navn” eller “-n”
  • beskrivelse - betydningen bak alternativet, for å hjelpe sluttbrukeren
  • kreves - om alternativet er obligatorisk, er standard falsk
  • arity - antall tilleggsparametere som alternativet bruker

La oss konfigurere en parameter Kunde ID i vårt målte faktureringsscenario:

@Parameter (names = {"--customer", "-C"}, beskrivelse = "ID for kunden som bruker tjenestene", arity = 1, obligatorisk = true) String customerId; 

La oss nå utføre kommandoen vår med den nye “–kunder” -parameteren:

$ java-app - kunde cust0000001A Les CustomerId: cust0000001A. 

På samme måte kan vi bruke den kortere “-C” -parameteren for å oppnå samme effekt:

$ java App -C cust0000001A Les CustomerId: cust0000001A. 

5.2. Nødvendige parametere

Der en parameter er obligatorisk, avsluttes applikasjonen å kaste a Parameter unntak hvis brukeren ikke spesifiserer det:

$ java App Unntak i tråden "main" com.beust.jcommander.ParameterException: Følgende alternativ kreves: [--customer | -C]

Vi bør merke oss at generelt sett eventuelle feil i parsing av parametrene resulterer i a Parameter unntak i JCommander.

6. Innebygde typer

6.1. IStringConverter Grensesnitt

JCommander utfører typekonvertering fra kommandolinjen String innspill i Java-typene i parameterklassene våre. De IStringConverter grensesnitt håndterer typekonvertering av en parameter fra String til hvilken som helst vilkårlig type. Så alle JCommanders innebygde omformere implementerer dette grensesnittet.

Ut av esken kommer JCommander med støtte for vanlige datatyper som String, Heltall, Boolsk, BigDecimal, og Enum.

6.2. Single Arity Typer

Arity er relatert til antall tilleggsparametere et alternativ bruker. JCommander's innebygde parametertyper har en standard arity på en, utenom Boolsk og Liste. Derfor er vanlige typer som String, Heltall, BigDecimal, Lang, og Enum, er single-arity typer.

6.3. Boolsk Type

Felt av typen boolsk eller Boolsk trenger ikke noen ekstra parameter - disse alternativene har en arity av null.

La oss se på et eksempel. Kanskje vi ønsker å hente kostnadene for en kunde, spesifisert etter abonnement. Vi kan legge til en boolsk felt spesifisert, som er falsk som standard:

@Parameter (names = {"--itemized"}) privat boolsk detaljert; 

Søknaden vår returnerer samlede kostnader med spesifisert satt til falsk. Når vi påkaller kommandolinjen med spesifisert parameter, setter vi feltet til ekte:

$ java App - gjenopplest Les flagg spesifisert: sant. 

Dette fungerer bra med mindre vi har en brukstilfelle der vi alltid vil ha spesifiserte avgifter, med mindre annet er spesifisert. Vi kan endre parameteren til å være ikkeItemisert, men det kan være tydeligere å kunne tilby falsk som verdien av spesifisert.

La oss introdusere denne oppførselen ved å bruke en standardverdi ekte for åkeren, og sette dens arity som en:

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

Nå, når vi spesifiserer alternativet, blir verdien satt til falsk:

$ java App - godkjent falsk Les flagg spesifisert: falsk. 

7. Liste Typer

JCommander gir noen få måter å binde argumenter på Liste Enger.

7.1. Spesifisere parameteren flere ganger

La oss anta at vi bare vil hente kostnadene for bare en delmengde av kundens abonnementer:

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

Feltet er ikke obligatorisk, og applikasjonen vil hente kostnadene på tvers av alle abonnementene hvis parameteren ikke leveres. Vi kan imidlertid spesifisere flere abonnementer ved å bruke parameternavnet flere ganger:

$ java App -S abonnementA001 -S abonnementA002 -S abonnementA003 Les abonnement: [abonnementA001, abonnementA002, abonnementA003]. 

7.2. Bindende Lister Bruke Splitter

I stedet for å spesifisere alternativet flere ganger, la oss prøve å binde listen ved å sende kommaseparert String:

$ java App -S abonnementA001, abonnementA002, abonnementA003 Les abonnementer: [abonnementA001, abonnementA002, abonnementA003]. 

Dette bruker en enkelt parameterverdi (arity = 1) for å representere en liste. JCommander vil bruke klassen CommaParameterSplitter for å binde det kommaseparerte String til vår Liste.

7.3. Bindende Lister Bruke en tilpasset splitter

Vi kan overstyre standard splitter ved å implementere IParameterSplitter grensesnitt:

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

Og deretter kartlegge implementeringen til splitter attributt i @Parameter:

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

La oss prøve det:

$ java App -S "abonnementA001: abonnementA002: abonnementA003" Les abonnement: [abonnementA001, abonnementA002, abonnementA003]. 

7.4. Variabel Arity Lister

Variabel arity tillater oss å erklærelister som kan ta ubestemte parametere, opp til neste alternativ. Vi kan stille inn attributtet variableArity som ekte for å spesifisere denne oppførselen.

La oss prøve dette for å analysere abonnementer:

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

Og når vi kjører kommandoen vår:

$ java App -S abonnementA001 abonnementA002 abonnementA003 - godkjente Leseabonnementer: [abonnementA001, abonnementA002, abonnementA003]. 

JCommander binder alle inngangsargumenter som følger alternativet “-S” til listefeltet, til neste alternativ eller slutten av kommandoen.

7.5. Fast arity Lister

Så langt har vi sett ubegrensede lister, der vi kan sende så mange listeelementer som vi ønsker. Noen ganger kan det være lurt å begrense antall varer som sendes til a Liste felt. For å gjøre dette kan vi spesifiser en helhetsaritetsverdi for a Liste feltfor å gjøre det avgrenset:

@Parameter (names = {"- abonnement", "-S"}, arity = 2) private List-abonnements-IDer; 

Fast arity tvinger en kontroll av antall parametere som sendes til a Liste alternativ og kaster a Parameter unntak i tilfelle brudd:

$ java App -S abonnementA001 abonnementA002 abonnementA003 Ble bestått hovedparameter 'abonnementA003', men ingen hovedparameter ble definert i arg-klassen din 

Feilmeldingen antyder at siden JCommander bare forventet to argumenter, prøvde den å analysere den ekstra inndataparameteren “subscriptionA003” som neste alternativ.

8. Egendefinerte typer

Vi kan også binde parametere ved å skrive tilpassede omformere. I likhet med innebygde omformere, må tilpassede omformere implementere IStringConverter grensesnitt.

La oss skrive en omformer for å analysere et ISO8601-tidsstempel:

klasse ISO8601TimestampConverter implementerer IStringConverter {privat statisk slutt DateTimeFormatter TS_FORMATTER = DateTimeFormatter.ofPattern ("uuuu-MM-dd'T'HH: mm: ss"); @Override public Instant convert (String value) {prøv {return LocalDateTime .parse (verdi, TS_FORMATTER) .atOffset (ZoneOffset.UTC) .toInstant (); } catch (DateTimeParseException e) {throw new ParameterException ("Ugyldig tidsstempel"); }}} 

Denne koden vil analysere inngangen String og returner en Umiddelbar, kaster en Parameter unntak hvis det er en konverteringsfeil. Vi kan bruke denne omformeren ved å binde den til et felt av typen Umiddelbar bruker omformer attributt i @Parameter:

@Parameter (names = {"--timestamp"}, converter = ISO8601TimestampConverter.class) private Øyeblikkelig tidsstempel; 

La oss se det i aksjon:

$ java-app - tidsstempel 2019-10-03T10: 58: 00 Les tidsstempel: 2019-10-03T10: 58: 00Z.

9. Valideringsparametere

JCommander gir noen få standardvalideringer:

  • om nødvendige parametere leveres
  • hvis antall spesifiserte parametere samsvarer med ariteten til et felt
  • om hver String parameter kan konverteres til det tilsvarende feltets type

I tillegg, Det kan være lurt å legge til tilpassede valideringer. La oss for eksempel anta at kunde-ID-ene må være UUID-er.

Vi kan skrive en validator for kundefeltet som implementerer grensesnittet IParameterValidator:

klasse UUIDValidator implementerer IParameterValidator {privat statisk sluttstreng UUID_REGEX = "[0-9a-fA-F] {8} (- [0-9a-fA-F] {4}) {3} - [0-9a-fA- F] {12} "; @Override public void validate (String name, String value) kaster ParameterException {if (! IsValidUUID (value)) {throw new ParameterException ("String parameter" + value + "is not a valid UUID."); }} privat boolsk isValidUUID (strengverdi) {retur Mønster.kompilere (UUID_REGEX) .matcher (verdi) .matches (); }} 

Så kan vi koble det opp med validateWith attributt til parameteren:

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

Hvis vi påkaller kommandoen med en ikke-UUID-kunde-ID, avsluttes applikasjonen med en valideringsfeilmelding:

$ java App --C customer001 Strengparameter customer001 er ikke et gyldig UUID. 

10. Underkommandoer

Nå som vi har lært om parameterbinding, la oss trekke alt sammen for å bygge kommandoene våre.

I JCommander kan vi støtte flere kommandoer, kalt underkommandoer, hver med et tydelig sett med alternativer.

10.1. @Parametere Kommentar

Vi kan bruke @Parametere for å definere underkommandoer. @Parametere inneholder attributtet commandNames for å identifisere en kommando.

La oss modell sende inn og hente som underkommandoer:

@Parameters (commandNames = {"send"}, commandDescription = "Send inn bruk for en gitt kunde og abonnement," + "godtar ett brukselement") klasse SubmitUsageCommand {// ...} @Parameters (commandNames = {"fetch" }, commandDescription = "Hent gebyrer for en kunde i den aktuelle måneden," + "kan spesifiseres eller samles") klasse FetchCurrentChargesCommand {// ...} 

JCommander bruker attributtene i @Parametere for å konfigurere underkommandoene, for eksempel:

  • commandNames - navnet på underkommandoen; binder kommandolinjeargumentene til klassen som er merket med @Parametere
  • commandDescription - dokumenterer formålet med underkommandoen

10.2. Legge til underkommandoer til JCommander

Vi legger til underkommandoene til JCommander med addCommand metode:

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

De addCommand metoden registrerer underkommandoene med sine respektive navn som spesifisert i commandNames attributt av @Parametere kommentar.

10.3. Analyse av underkommandoer

For å få tilgang til brukerens valg av kommando, må vi først analysere argumentene:

jc.parse (args); 

Deretter kan vi trekke ut underkommandoen med getParsedCommand:

Streng parsedCmdStr = jc.getParsedCommand (); 

I tillegg til å identifisere kommandoen, binder JCommander resten av kommandolinjeparametrene til sine felt i underkommandoen. Nå må vi bare ringe kommandoen vi vil bruke:

switch (parsedCmdStr) {case "submit": submitUsageCmd.submit (); gå i stykker; sak "fetch": fetchChargesCmd.fetch (); gå i stykker; standard: System.err.println ("Ugyldig kommando:" + parsedCmdStr); } 

11. JCommander Usage Help

Vi kan påberope oss bruk å gjengi en bruksanvisning. Dette er et sammendrag av alle alternativene som applikasjonen bruker. I søknaden vår kan vi påberope bruk på hovedkommandoen, eller alternativt på hver av de to kommandoene "sende" og "hente" hver for seg.

En bruksdisplay kan hjelpe oss på et par måter: viser hjelpealternativer og under feilhåndtering.

11.1. Viser hjelpealternativer

Vi kan binde et hjelpealternativ i kommandoene våre ved hjelp av a boolsk parameter sammen med attributtet hjelp satt til ekte:

@Parameter (names = "--help", help = true) privat boolsk hjelp; 

Deretter kan vi oppdage om “–help” har blitt gitt i argumentene, og ringe bruk:

hvis (cmd.help) {jc.bruk (); } 

La oss se hjelpeproduksjonen for underkommandoen "send":

$ java App sende --hjelp Bruk: send [alternativer] Alternativer: * - kunde, -C ID for kunden som bruker tjenestene * - abonnement, -S ID for abonnementet som ble kjøpt * - mengde brukt antall ; rapportert antall er lagt til over faktureringsperioden * --pristype, -P Pristype for rapportert bruk (verdier: [PRE_RATED, UNRATED]) * --tidsstempel Tidsstempel for brukshendelsen, må ligge i gjeldende faktureringsperiode -pris Hvis PRE_RATED, skal enhetspris påføres per rapportert bruksenhet 

De bruk metoden bruker @Parameter attributter som beskrivelse for å vise et nyttig sammendrag. Parametere merket med en stjerne (*) er obligatoriske.

11.2. Feilhåndtering

Vi kan fange Parameter unntak og ring bruk for å hjelpe brukeren til å forstå hvorfor deres innspill var feil. Parameter unntak inneholder JCommander forekomst for å vise hjelpen:

prøv {jc.parse (args); } fange (ParameterException e) {System.err.println (e.getLocalizedMessage ()); jc.bruk (); } 

12. Konklusjon

I denne opplæringen brukte vi JCommander til å bygge et kommandolinjeprogram. Mens vi dekket mange av de viktigste funksjonene, er det mer i den offisielle dokumentasjonen.

Som vanlig er kildekoden for alle eksemplene tilgjengelig på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found