Een overzicht van Batch Processing in Java EE 7.0

onderzoek de nieuwe batch processing mogelijkheden van JSR 352 voor Java EE 7.

batchverwerking wordt in veel industrieën gebruikt voor taken variërend van payrollverwerking; statement generatie; end-of-day taken zoals renteberekening en ETL (extract, load, and transform) in een datawarehouse; en nog veel meer. Typisch, batchverwerking is bulk-georiënteerd, niet-interactief, en langdurig—en kan data – of berekening-intensief zijn. Batch-taken kunnen op schema worden uitgevoerd of op aanvraag worden gestart. Aangezien batchtaken doorgaans langlopende taken zijn, zijn controle wijzen en opnieuw opstarten ook veelvoorkomende functies in batchtaken.

JSR 352 (Batch Processing for Java Platform), onderdeel van het onlangs geïntroduceerde Java EE 7 platform, definieert het programmeermodel voor batch-toepassingen plus een runtime om batch-taken uit te voeren en te beheren. Dit artikel behandelt een aantal van de belangrijkste concepten, waaronder functie highlights, een overzicht van geselecteerde API ‘ s, de structuur van Taakspecificatie taal, en een voorbeeld batch toepassing. Het artikel beschrijft ook hoe u batch-toepassingen kunt uitvoeren met behulp van GlassFish Server Open Source Edition 4.0.

Batch Processing Architecture

Deze sectie en Figuur 1 beschrijven de basiscomponenten van de batch processing architecture.

figuur 1

figuur 1

  • een taak omvat het hele batchproces. Een taak bevat een of meer stappen. Een taak wordt samengesteld met behulp van een Job Specification Language (JSL) die de volgorde specificeert waarin de stappen moeten worden uitgevoerd. In JSR 352 wordt JSL gespecificeerd in een XML-bestand genaamd het job XML-bestand. Kortom, een taak (met JSR 352) is in principe een container voor stappen.
  • een step is een domeinobject dat een onafhankelijke, sequentiële fase van de taak inkapselt. Een stap bevat alle benodigde logica en gegevens om de daadwerkelijke verwerking uit te voeren. De batchspecificatie laat opzettelijk de definitie van een stap vaag omdat de inhoud van een stap puur toepassingsspecifiek is en zo complex of eenvoudig kan zijn als de ontwikkelaar wenst. Er zijn twee soorten stappen: chunk en batchlet.
    • een stap in chunkstijl bevat precies één ItemReader, één ItemProcessor, en één ItemWriter. In dit patroon leest ItemReader één item tegelijk, ItemProcessor verwerkt het item op basis van de business logica (zoals “bereken het saldo van de rekening”), en geeft het aan de batch runtime voor aggregatie. Zodra het” chunk-size”aantal items wordt gelezen en verwerkt, worden ze gegeven aan een ItemWriter, die de gegevens schrijft (bijvoorbeeld naar een databasetabel of een plat bestand). De transactie wordt dan vastgelegd.
    • JSR 352 definieert ook een rol-je-eigen soort van een stap die een batchlet wordt genoemd. Een batchlet is vrij om alles te gebruiken om de stap te bereiken, zoals het verzenden van een e-mail.
  • JobOperator biedt een interface om alle aspecten van taakverwerking te beheren, inclusief operationele commando ‘ s, zoals start, herstart en stop, evenals taakopslagopdrachten, zoals het ophalen van taak-en step-uitvoeringen. Zie paragraaf 10.4 van de JSR 352 specificatie voor meer details over JobOperator.
  • JobRepository bevat informatie over taken die momenteel worden uitgevoerd en taken die in het verleden zijn uitgevoerd. JobOperator biedt API ‘ s om toegang te krijgen tot deze repository. Een JobRepository kan worden geïmplementeerd met behulp van bijvoorbeeld een database of een bestandssysteem.

het ontwikkelen van een eenvoudige Payroll Processing applicatie

Dit artikel toont enkele van de belangrijkste kenmerken van JSR 352 met behulp van een eenvoudige payroll processing applicatie. De aanvraag is bewust heel eenvoudig gehouden om zich te concentreren op de kernbegrippen van JSR 352.

de SimplePayrollJob batchtaak omvat het lezen van invoergegevens voor loonadministratie uit een CSV-bestand (comma-separated values). Elke regel in het bestand bevat een werknemer-ID en het basissalaris (per maand) voor een werknemer. De batch job berekent vervolgens de belasting die moet worden ingehouden, de bonus, en het netto salaris. De baan moet eindelijk de verwerkte payroll records in een database tabel te schrijven.

we gebruiken een CSV-bestand in dit voorbeeld alleen maar om aan te tonen dat JSR 352 batch-toepassingen toestaat om te lezen en te schrijven vanuit elke willekeurige bron.

Taakspecificatie taal voor de Payroll Processing applicatie

we bespraken dat een step een domeinobject is dat een onafhankelijke, sequentiële fase van de taak inkapselt, en dat een taak in principe een container is voor een of meer stappen.

in JSR 352 specificeert een JSL in principe de volgorde waarin stappen moeten worden uitgevoerd om de taak te volbrengen. De JSL is krachtig genoeg om voorwaardelijke uitvoering van stappen toe te staan, en het staat ook toe dat elke stap zijn eigen eigenschappen heeft, luisteraars, enzovoort.

een batch-toepassing kan zoveel JSLs hebben als het wil, waardoor het zoveel batch-taken kan starten als nodig is. Een applicatie kan bijvoorbeeld twee JSLs hebben, een voor payroll verwerking en een andere voor het genereren van rapporten. Elke JSL moet een unieke naam hebben en moet in de META-INF/batch-jobs map worden geplaatst. Submappen onder META-INF/batch-jobs worden genegeerd.

onze JSL voor payroll verwerking wordt geplaatst in een bestand genaamd SimplePayrollJob.xml en ziet eruit als lijst 1:

 <job xmlns=http://xmlns.jcp.org/xml/ns/javaee version="1.0"> <step> <chunk item-count="2"> <reader ref="simpleItemReader/> <processor ref="simpleItemProcessor/> <writer ref="simpleItemWriter/> </chunk> </step></job>

Listing 1

onze SimplePayrollJobbatch-taak heeft slechts één stap (genaamd”proces”). Het is een chunk-stijl stap en heeft (zoals vereist voor een chunk-stijl stap), een ItemReader, een ItemProcessor, en een ItemWriter. De implementaties voor ItemReaderItemProcessor, en ItemWriter voor deze stap zijn opgegeven met de ref attribuut in de <reader><processor>, en <writer> – elementen.

wanneer de taak wordt verzonden (we zullen later zien hoe batch-taken worden verzonden), begint de batch runtime met de eerste stap in de JSL en loopt zijn weg door totdat de volledige taak is voltooid of een van de stappen mislukt. De JSL is krachtig genoeg om zowel voorwaardelijke stappen als parallelle uitvoering van stappen toe te staan, maar we zullen deze details in dit artikel niet behandelen.

Het item-count attribuut, dat wordt gedefinieerd als 2 In Lijst 1, definieert de grootte van het stuk.

Hier is een overzicht op hoog niveau van hoe chunk-stijl stappen worden uitgevoerd. Zie paragraaf 11.6 (“regelmatige verwerking van brokken”) van de JSR 352-specificatie voor meer details.

  1. Start een transactie.
  2. roep deItemReader aan en geef het item gelezen door deItemReader door aan deItemProcessorItemProcessor verwerkt het item en retourneert het verwerkte item naar de batch runtime.
  3. de batch runtime herhaalt Stap 2 item-count tijden en houdt een lijst bij van verwerkte items.
  4. de batch runtime roept het ItemWriter aan dat item-count aantal verwerkte items schrijft.
  5. als uitzonderingen worden gegooid van ItemReaderItemProcessor, of ItemWriter, mislukt de transactie en wordt de stap gemarkeerd als ” mislukt.”Zie paragraaf 5.2.1.2.1 (“uitzonderingen overslaan”) in de JSR 352-specificatie.
  6. als er geen uitzonderingen zijn, verkrijgt de batch runtime checkpoint data van ItemReader en ItemWriter (zie paragraaf 2.5 in de JSR 352 specificatie voor meer details). De batch runtime commit de transactie.
  7. stappen 1 tot en met 6 worden herhaald als de ItemReader meer gegevens heeft om te lezen.

Dit betekent dat in ons voorbeeld de batch runtime twee records zal lezen en verwerken en de ItemWriter twee records per transactie zal wegschrijven.

Schrijven ItemReaderItemProcessor, en ItemWriter

Schrijven ItemReader

Onze payroll verwerking van de batch JSL definieert één brok stijl stap en geeft aan dat de stap maakt gebruik van een ItemReader naam simpleItemReader. Onze applicatie bevat een implementatie van ItemReader om input CSV data te lezen. Lijst 2 toont een fragment van onze ItemReader:

 @Namedpublic class SimpleItemReader extends AbstractItemReader { @Inject private JobContext jobContext; ...}

Listing 2

merk op dat de klasse is geannoteerd met de @Named annotatie. Omdat de@Named annotatie de standaardwaarde gebruikt, is de contexten en Dependency Injection (CDI) naam voor deze beansimpleItemReader. De JSL specificeert de CDI naam van hetItemReader in het<reader> element. Hierdoor kan de batch runtime (via CDI) onze ItemReader instantiëren wanneer de stap wordt uitgevoerd.

onze ItemReader injecteert ook een JobContextJobContext staat het batch-artefact (ItemReader, in dit geval) toe om waarden te lezen die zijn doorgegeven tijdens het indienen van taken.

onze payroll SimpleItemReader overschrijft de open() methode om de invoer te openen waaruit de payroll-invoergegevens worden gelezen. Zoals we later zullen zien, zal de parameter prevCheckpointInfo niet null zijn als de taak opnieuw wordt opgestart.

in ons voorbeeld opent de open() methode, die wordt weergegeven in Lijst 3, het payroll invoerbestand (dat samen met de toepassing is verpakt).

public void open(Serializable prevCheckpointInfo) throws Exception { JobOperator jobOperator = BatchRuntime.getJobOperator(); Properties jobParameters = jobOperator.getParameters(jobContext.getExecutionId()); String resourceName = (String) jobParameters.get("payrollInputDataFileName"); inputStream = new FileInputStream(resourceName); br = new BufferedReader(new InputStreamReader(inputStream)); if (prevCheckpointInfo != null) recordNumber = (Integer) prevCheckpointInfo; for (int i=1; i<recordNumber; i++) { //Skip upto recordNumber br.readLine(); } System.out.println(" Opened Payroll file for reading from record number: " + recordNumber); }

Listing 3

De readItem()methode leest in principe één regel met gegevens uit het invoerbestand en bepaalt of de regel twee gehele getallen bevat (een voor werknemers-ID en een voor basissalaris). Als er twee gehele getallen zijn, maakt en retourneert het een nieuwe instantie van PayrollInputRecord en keert terug naar de batch runtime (die vervolgens wordt doorgegeven aan ItemWriter).

public Object readItem() throws Exception { Object record = null; if (line != null) { String fields = line.split("+"); PayrollInputRecord payrollInputRecord = new PayrollInputRecord(); payrollInputRecord.setId(Integer.parseInt(fields)); payrollInputRecord.setBaseSalary(Integer.parseInt(fields)); record = payrollInputRecord; //Now that we could successfully read, Increment the record number recordNumber++; } return record;}

Listing 4

de methode checkpointInfo()wordt aangeroepen door de batch runtime aan het einde van elke succesvolle chunktransactie. Hiermee kan de lezer de laatste succesvolle leespositie controleren.

in ons voorbeeld geeft de checkpointInfo() de recordNumber het aantal records aan dat met succes is gelezen, zoals weergegeven in lijst 5.

@Overridepublic Serializable checkpointInfo() throws Exception { return recordNumber;}

Listing 5

schrijven van de ItemProcessor

onze SimpleItemProcessor volgt een patroon dat lijkt op het patroon voor SimpleItemReader.

de processItem() methode ontvangt (uit de batch runtime) de PayrollInputRecord. Het berekent vervolgens de belasting en het net en geeft een PayrollRecord terug als output. Merk in lijst 6 op dat het type object dat wordt geretourneerd door een ItemProcessor heel anders kan zijn dan het type object dat het ontving van ItemReader.

 @Namedpublic class SimpleItemProcessor implements ItemProcessor { @Inject private JobContext jobContext; public Object processItem(Object obj) throws Exception { PayrollInputRecord inputRecord = (PayrollInputRecord) obj; PayrollRecord payrollRecord = new PayrollRecord(); int base = inputRecord.getBaseSalary(); float tax = base * 27 / 100.0f; float bonus = base * 15 / 100.0f; payrollRecord.setEmpID(inputRecord.getId()); payrollRecord.setBase(base); payrollRecord.setTax(tax); payrollRecord.setBonus(bonus); payrollRecord.setNet(base + bonus - tax); return payrollRecord; } }

Listing 6

schrijven van de ItemWriter

nu moet SimpleItemWriter voorspelbare regels voor u volgen.

het enige verschil is dat het een EntityManager injecteert, zodat het de PayrollRecord instanties (die JPA-entiteiten zijn) in een database kan laten bestaan, zoals weergegeven in Lijst 7.

 @Namedpublic class SimpleItemWriter extends AbstractItemWriter { @PersistenceContext EntityManager em; public void writeItems(List list) throws Exception { for (Object obj : list) { System.out.println("PayrollRecord: " + obj); em.persist(obj); } }}

Listing 7

De writeItems()methode houdt allePayrollRecordinstanties in een databasetabel aan met behulp van JPA. Er zullen ten hoogste item-count items (de chunk grootte) in de lijst staan.

nu we onze JSL, ItemReaderItemProcessor, en ItemWriter klaar hebben, laten we eens kijken hoe een batch-taak kan worden ingediend.

een Batch-taak starten met een Servlet

merk op dat de aanwezigheid van een taak XML-bestand of andere batch-artefacten (zoals ItemReader) niet betekent dat een batch-taak automatisch wordt gestart wanneer de toepassing wordt geïmplementeerd. Een batch-taak moet expliciet worden gestart, laten we zeggen, vanaf een servlet of van een Enterprise JavaBeans (EJB) timer of een EJB business methode.

in onze payroll-applicatie gebruiken we een servlet (genaamd PayrollJobSubmitterServlet) om een batch-taak in te dienen. De servlet toont een HTML-pagina die aan de gebruiker een formulier met twee knoppen presenteert. Wanneer op de eerste knop, genaamd Bereken Payroll, wordt geklikt, roept de servlet de startNewBatchJob methode aan, weergegeven in lijst 8, die een nieuwe batch-taak start.

 private long startNewBatchJob()throws Exception { JobOperator jobOperator = BatchRuntime.getJobOperator(); Properties props = new Properties(); props.setProperty("payrollInputDataFileName", payrollInputDataFileName); return jobOperator.start(JOB_NAME, props);}

Listing 8

de eerste stap is het verkrijgen van een instantie van JobOperator. Dit kan worden gedaan door het volgende te bellen::

JobOperator jobOperator = BatchRuntime.getJobOperator();

de servlet maakt dan een Properties object en slaat de naam van het invoerbestand erin op. Tot slot wordt een nieuwe batchtaak gestart door het volgende aan te roepen:

jobOperator.start(jobName, properties)

de jobname is niets anders dan de JSL XML-bestandsnaam van de taak (minus de .xml extensie). De parameter properties dient om alle invoergegevens door te geven aan de taak. HetProperties object (dat de naam van het payroll-invoerbestand bevat) wordt beschikbaar gemaakt voor andere batchartefacten (zoalsItemReaderItemProcessor, enzovoort) via deJobContext interface.

de batch runtime kent een unieke ID toe, genaamd de execution ID, om elke uitvoering van een taak te identificeren of het een pas aangeleverde taak is of een herstart taak. Veel van de JobOperator methoden nemen de execution ID als parameter. Met behulp van de execution ID kan een programma de huidige (en vroegere) uitvoeringsstatus en andere statistieken over de taak verkrijgen. De methode JobOperator.start() geeft het uitvoer-ID terug van de taak die is gestart.

Details over Batch-taken ophalen

wanneer een batch-taak wordt ingediend, maakt de batchruntime een instantie van JobExecution om deze te volgen. JobExecution heeft methoden om verschillende details te verkrijgen, zoals de starttijd van de taak, de voltooiingstijd van de taak, de status van taakuitgangsfunctie, enzovoort. Om de JobExecution voor een uitvoer-ID te verkrijgen, kunt u de methode JobOperator.getJobExecution(executionId) gebruiken. Listing 9 toont de definitie van JobExecution:

 package javax.batch.runtime;public interface JobExecution { long getExecutionId(); java.lang.String getJobName(); javax.batch.runtime.BatchStatus getBatchStatus(); java.util.Date getStartTime(); java.util.Date getEndTime(); java.lang.String getExitStatus(); java.util.Date getCreateTime(); java.util.Date getLastUpdatedTime(); java.util.Properties getJobParameters();}

Listing 9

Verpakking de Toepassing

Nu hebben we onze JSL, ItemReaderItemProcessorItemWriter, en onze servlet klaar, het is tijd om te verpakken en klaar te implementeren.

u kunt uw batchtoepassing implementeren als een van de ondersteunde Java EE-Archieven (bijvoorbeeld .war.jar, of .ear). U kunt uw batch artefact klassen bundelen samen met andere Java EE klassen (zoals EJB bonen en servlets).

de enige speciale vereiste is dat u uw taak JSLs onder de META-INF/batch-jobs map voor .jar bestanden moet plaatsen. Voor .war archieftypen, plaatst u uw taak JSLs onder de WEB-INF/classes/META-INF/batch-jobs map.

implementeren en uitvoeren van de Payroll Sample applicatie in GlassFish 4.0

laten we de payroll applicatie die we hebben ontwikkeld implementeren in de GlassFish 4.0 application server. GlassFish 4.0 is de referentie implementatie (RI)voor de Java EE 7.0 specificatie en bevat ook de RI voor JSR 352. U kunt meer informatie vinden over GlassFish 4.0 bij http://glassfish.org en over de Java Batch 1.0 RI bij https://javaee.github.io/.

GlassFish 4.0

U kunt GlassFish 4.0 downloaden van https://glassfish.java.net/download.html en vervolgens installeren. Start GlassFish 4.0 door een opdrachtvenster te openen en het volgende commando uit te voeren:

<GlassFish Install Dir>/bin/asadmin start-domain

omdat de sample payroll applicatie een database gebruikt (om verwerkte gegevens uit te schrijven), hebben we een database nodig voordat we onze applicatie kunnen uitvoeren. U kunt de Apache Derby database starten door het volgende commando uit te voeren:

<GlassFish Install Dir>/bin/asadmin start-database

compileren, verpakken en implementeren van de Payroll applicatie

maak eerst een nieuwe map aan met de naam hello-batch. Ga dan naar dehello-batch map:

cd hello-batch

om te compileren en te verpakken, voer je het volgende commando uit, dat hello-batch.war aanmaakt onder de doelmap:

mvn clean package

om hello-batch.war, voer het volgende commando uit:

<GlassFish Install Dir>/bin/asadmin deploy target/hello-batch.war

als u de toepassing wilt herschikken, kunt u het volgende commando uitvoeren:

<GlassFish Install Dir>/bin/asadmin deploy -force target/hello-batch.war

het uitvoeren van de Payroll-toepassing

zodra u het hello-batch.war bestand implementeert, kunt u de toepassing uitvoeren door http://localhost:8080/hello-batch/PayrollJobSubmitterServlet vanuit een browser te benaderen. Toegang tot deze URL moet het scherm weergegeven in Figuur 2.

Figuur 2

Figuur 2

klik op de knop Payroll berekenen en u ziet een nieuwe vermelding in de tabel, zoals weergegeven in Figuur 3.

Figuur 3

Figuur 3

klik op de knop Vernieuwen en u zou de Afsluitstatus en Eindtijdkolommen bijgewerkt moeten zien voor de laatste taak (zie Figuur 4). In de kolom Afsluitstatus wordt weergegeven of de taak is mislukt of voltooid. Omdat onze SimplePayrollJob geen fouten bevat (tenminste nog niet!), de Exit Status wordt weergegeven voltooid.

Figuur 4

Figuur 4

Klik nog een paar keer op de knoppen Payroll berekenen en vernieuwen. Merk op dat elke keer dat een taak wordt gestart, een nieuwe uitvoering-ID (en instantie-ID) wordt gegeven aan de taak, zoals weergegeven in Figuur 5.

Figuur 5

Figuur 5

herstarten van mislukte taken

tot nu toe hadden we batchtaken gestart met behulp van de jobOperator.start() methode. Laten we zeggen dat onze payroll input bestand heeft een aantal fouten. Ofwel de ItemReader of de ItemProcessor kan ongeldige records detecteren en de huidige stap en de taak falen. De beheerder of de eindgebruiker kan de fout herstellen en kan de batch-taak opnieuw opstarten. Deze aanpak van de lancering van een nieuwe taak die begint vanaf het begin na het herstellen van fouten misschien niet schaal als de hoeveelheid gegevens te verwerken is groot. JobOperator biedt een andere methode genaamd restart() om precies dit probleem op te lossen.

snel overzicht van JobInstance en JobExecution

We zagen eerder dat een taak in wezen een container voor stappen is. Wanneer een taak wordt gestart, moet deze worden gevolgd, zodat de batch runtime een JobInstanceaanmaakt. Een JobInstance verwijst naar het concept van een logische run. In ons voorbeeld hebben we een PayrollJob en als de PayrollJob elke maand wordt uitgevoerd, zal er een Jan-2013 JobInstance zijn en zal er een andere Feb-2013 JobInstance zijn, enzovoort.

als de payrollverwerking voor Jan-2013 mislukt, moet deze opnieuw worden gestart (na vermoedelijk de fout te hebben hersteld), maar het is nog steeds de jan-2013 run omdat het nog steeds jan-2013 records verwerkt.

A JobExecution verwijst naar het concept van een enkele poging om een taak uit te voeren. Elke keer dat een taak wordt gestart of herstart, wordt een nieuwe JobExecution aangemaakt die behoort tot dezelfde JobInstance. In ons voorbeeld, als de Jan-2013 JobInstance herstart wordt, is het nog steeds dezelfde Jan-2013 JobInstance maar een nieuwe JobExecution wordt gemaakt die behoort tot dezelfde JobInstance.

samengevat kan een taak een of meer instanties van JobInstance hebben en elke JobInstance kan een of meer JobExecution instanties hebben. Het gebruik van een nieuwe JobInstance betekent “begin vanaf het begin” en het gebruik van een bestaande JobInstance betekent in het algemeen “begin vanaf waar je gestopt was.”

hervatten van mislukte taken

als u zich herinnert, wordt een stap in chunk-stijl uitgevoerd in een transactie waarin item-count items worden gelezen, verwerkt en geschreven. Nadat de ItemWriter’s writeItems() is aangeroepen, roept de batch runtime de checkpointInfo() methode aan op zowel ItemReader en ItemWriter. Dit staat zowel ItemReader en ItemWriter toe om een bladwijzer te maken (opslaan) van hun huidige voortgang. De gegevens die als bladwijzer zijn gemarkeerd voor een ItemReader kunnen alles zijn wat het kan helpen om het lezen te hervatten. Bijvoorbeeld, onze SimpleItemReader moet het regelnummer opslaan waartoe het tot nu toe succesvol heeft gelezen.

paragraaf 10.8 van de JSR 352 specificatie beschrijft de herstart verwerking in detail.

laten we een moment nemen om in het logbestand te kijken waar onze SimpleItemReader enkele nuttige berichten uitbrengt van de open() en checkpoint() methoden. Elk bericht wordt voorafgegaan door de tekenreeks zodat u snel de berichten kunt identificeren. Het logbestand bevindt zich op <GlassFish install Dir>/domains/domain1/logs/server.log.

Listing 10 toont de berichten die zijn voorafgegaan door de tekenreeks :

 Opened Payroll File. Will start reading from record number: 0]] checkpointInfo() called. Returning current recordNumber: 2]] checkpointInfo() called. Returning current recordNumber: 4]] checkpointInfo() called. Returning current recordNumber: 6]] checkpointInfo() called. Returning current recordNumber: 8]] checkpointInfo() called. Returning current recordNumber: 9]] close called.]]

Listing 10

Opmerking: U kunt ook het commando tail -f server.log | grep SimpleItemReadergebruiken.

omdat ons taak XML-bestand (SimplePayrollJob.xml) een waarde specificeert van 2 voor item-count als de chunkgrootte, roept de batchruntime checkpointInfo() op onze ItemReader elke twee records. De batch runtime slaat deze controlepuntinformatie op in JobRepository. Dus, als er een fout optreedt tijdens het midden van onze chunk verwerking, de batch applicatie moet in staat zijn om te hervatten vanaf de laatste succesvolle checkpoint.

laten we enkele fouten in ons invoerbestand introduceren en zien hoe we kunnen herstellen van invoerfouten.

Als u kijkt naar de uitvoer van onze servlet, die zich bevindt onder <GlassFish install Dir>/domains/domain1/applications/hello-batch/WEB-INF/classes/payroll-data/payroll-data.csv, ziet u dat het de locatie weergeeft van het invoerbestand van waaruit CSV-gegevens worden gelezen voor onze payroll-toepassing. Listing 11 toont de inhoud van het bestand:

1, 81002, 82003, 83004, 84005, 85006, 86007, 87008, 88009, 8900

Listing 11

Open uw favoriete editor en voer een fout in. Bijvoorbeeld, laten we zeggen dat we een paar tekens toevoegen aan het salarisveld op het achtste record, zoals weergegeven in Lijst 12:

1, 81002, 82003, 83004, 84005, 85006, 86007, 87008, abc88009, 8900

Listing 12

sla het bestand op en sluit de editor. Ga terug naar uw browser en klik op de knop Payroll berekenen gevolgd door de knop Vernieuwen. U zou zien dat de onlangs ingediende taak mislukt is, zoals weergegeven in Figuur 6. (Kijk naar de kolom Exit Status.)

Figuur 6

Figuur 6

u zult ook merken dat er een herstartknop verschijnt naast het uitvoer-ID van de zojuist mislukte taak. Als u op Vernieuwen klikt, zal de taak mislukken (omdat we het probleem nog niet hebben opgelost). Figuur 7 laat zien wat er wordt weergegeven na een paar klikken op de knop Vernieuwen.

Figuur 7

Figuur 7

Als u in het GlassFish-serverlog kijkt (onder <GlassFish install Dir>/domains/domain1/logs/server.log), zult u een uitzondering zien, zoals weergegeven in lijst 13:

Caught exception executing step: com.ibm.jbatch.container.exception.BatchContainerRuntimeException: Failure in Read-Process-Write Loop......Caused by: java.lang.NumberFormatException: For input string: "abc8800" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Integer.parseInt(Integer.java:492) at java.lang.Integer.parseInt(Integer.java:527) at com.oracle.javaee7.samples.batch.hello.SimpleItemReader.readItem(SimpleItemReader.java:100) 

listing 13

u moet ook merken dat wanneer u op de knop Herstart klikt, een nieuwe taakuitvoering wordt gemaakt, maar de ID van de taakinstantie blijft hetzelfde. Wanneer u op de knop Vernieuwen klikt, roept onze PayrollJobSubmitter servlet een methode aan met de naam restartBatchJob(), die wordt weergegeven in lijst 14:

private long restartBatchJob(long lastExecutionId) throws Exception { JobOperator jobOperator = BatchRuntime.getJobOperator(); Properties props = new Properties(); props.setProperty("payrollInputDataFileName", payrollInputDataFileName); return jobOperator.restart(lastExecutionId, props);}

lijst 14

de belangrijkste regel in lijst 14 is de aanroep naar JobOperator’s restart() methode. Deze methode neemt eenProperties object, net alsstart(), maar in plaats van het doorgeven van een taak XML-bestandsnaam, geeft het de uitvoering-ID van de laatst mislukte taak. Met behulp van de execution ID van de meest recent mislukte taak kan de batch runtime het laatste succesvolle controlepunt van de vorige uitvoering ophalen. De opgehaalde checkpointgegevens worden doorgegeven aan de open() methode van onze SimpleItemReader (en ItemWriter) om hen in staat te stellen verder te lezen (en te schrijven) vanaf het laatste succesvolle checkpoint.

terwijl u ervoor zorgt dat uw browser de pagina toont met een herstartknop, bewerk het bestand opnieuw en verwijder de vreemde tekens uit het achtste record. Klik vervolgens op de herstart en ververs knoppen. De laatste uitvoering moet een voltooide status weergeven, zoals weergegeven in Figuur 8.

Figuur 8

Figuur 8

Het is tijd om in het logbestand te kijken om te begrijpen wat er zojuist is gebeurd. Nogmaals, op zoek naar berichten die vooraf zijn gegaan met SimpleItemReader, de lijst 15 toont wat je zou kunnen zien:

 Opened Payroll File. Will start reading from record number: 7]] checkpointInfo() called. Returning current recordNumber: 9]] checkpointInfo() called. Returning current recordNumber: 10]] close called.]]

Listing 15

zoals u kunt zien, werd onze SimpleItemReader‘sopen()methode aangeroepen met de vorige checkpoint waarde (dat recordnummer 7 was) waardoor onzeSimpleItemReaderom de eerste zes records over te slaan en het lezen van het zevende record te hervatten.

Batch-taken bekijken met behulp van de GlassFish 4.0-beheerconsole

kunt u de lijst van alle batch-taken bekijken in de JobRepository. Start een browservenster op en ga naar localhost:4848. Klik vervolgens op server (Admin Server) in het linkerpaneel, zoals weergegeven in Figuur 9.

figuur 9

figuur 9

u kunt op het tabblad Batch klikken, dat alle batch-taken moet weergeven die naar deze GlassFish-server zijn verzonden. Merk op dat de JobRepository geà mplementeerd wordt met behulp van een database en dat daarom de taakgegevens van de GlassFish 4.0-server opnieuw worden opgestart. Figuur 10 toont alle batch-taken in de JobRepository.

Figuur 10

Figuur 10

u kunt ook op een van de ID ’s klikken die worden vermeld onder Uitvoer-ID’ s. Als u bijvoorbeeld op 293 klikt, ziet u details over die uitvoering:

Figuur 11

Figuur 11

Meer details over de uitvoering kunnen worden verkregen door op het tabblad Uitvoeringsstappen bovenaan te klikken.

Figuur 12

Figuur 12

Kijk naar de statistieken op deze pagina. Het laat zien hoeveel reads, writes en commits werden uitgevoerd tijdens deze uitvoering.

Batch-taken weergeven met GlassFish 4.0 CLI

U kunt ook de details bekijken over taken die in GlassFish 4 worden uitgevoerd.0 server met behulp van de command-line interface (CLI).

om de lijst Te bekijken van batch jobs, open een commando-venster en voer het volgende commando uit:

asadmin list-batch-jobs -l

Je moet het zien uitvoer gelijkaardig aan Figuur 13:

Figuur 13

Figuur 13

om de lijst Te bekijken van batch JobExecutions, kunt u voer dit commando uit:

asadmin list-batch-job-executions -l

Je moet het zien uitvoer gelijkaardig aan Figuur 14:

Figuur 14

Figuur 14

het commando toont de voltooiingsstatus van elke uitvoering en ook de taakparameters die aan elke uitvoering worden doorgegeven.

ten slotte, om details te zien over elke stap in een JobExecution, kunt u het volgende commando gebruiken:

asadmin list-batch-job-steps -l

u zou uitvoer moeten zien vergelijkbaar met figuur 15:

figuur 15

figuur 15

neem nota van de kolom stepmetrics. Het vertelt hoe vaak ItemReader en ItemWriter werden aangeroepen en ook hoeveel commits en rollbacks werden gedaan. Dit zijn zeer waardevolle statistieken.

de CLI-uitvoer moet overeenkomen met de beheerconsole-weergave omdat beide dezelfde JobRepositoryopvragen.

u kunt asadmin help <command-name> gebruiken om meer details over de CLI-opdrachten te krijgen.

conclusie

In dit artikel zagen we hoe eenvoudige batch-toepassingen te schrijven, te verpakken en uit te voeren die stappen in chunkstijl gebruiken. We zagen ook hoe de checkpoint-functie van de batch runtime zorgt voor de eenvoudige herstart van mislukte batch-taken. Toch hebben we nauwelijks de oppervlakte van JSR 352 bekrast. Met de volledige set van Java EE componenten en functies tot uw beschikking, waaronder servlets, EJB bonen, CDI bonen, EJB automatische timers, en ga zo maar door, feature-rijke batch toepassingen kunnen vrij gemakkelijk worden geschreven.

Dit artikel behandelde ook (kort) de GlassFish 4.0-beheerconsole en CLI-ondersteuning voor het opvragen van de batch JobRepository. Zowel de Admin Console als de CLI bieden waardevolle details over taken en stappen die kunnen worden gebruikt om potentiële knelpunten op te sporen.

JSR 352 ondersteunt veel meer opwindende functies, zoals batchlets, splits, flows en aangepaste checkpoints, die zullen worden behandeld in toekomstige artikelen.

zie ook

JSR 352

over de auteur

Mahesh Kannan is een senior Software engineer bij Oracle ‘ s Cloud Application Foundation team, en hij is de Expert Group Member voor de Java Batch JSR. Door zijn uitgebreide ervaring met applicatieservers, containers en gedistribueerde systemen heeft hij als lead architect en “consultant at large” gewerkt aan vele projecten die innovatieve oplossingen voor Oracle-producten ontwikkelden.

doe mee aan het gesprek

doe mee aan het gesprek met de Java-gemeenschap op Facebook, Twitter en het Oracle Java Blog!

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.