2015-02-02 5 views
0

Я хочу иметь возможность установить статус шага и задания в состояние сбоя, когда писатель выдает исключения. После некоторой отладки и изучения исходного исходного кода весны я заметил, что RepeatTemplate сконфигурирован с SimpleRetryExceptionHandler, который считает BatchRuntimeException фатальным исключением и, следовательно, устанавливает статус задания в FAILED, поэтому я завернул код в своем авторе в попытке -catch, который обертывает RuntimeException в BatchRuntimeException, и теперь статусы задания и шага устанавливаются как FAILED, как я и хотел. Я не уверен, что это правильный способ сделать это, хотя я не мог найти его документированным где-либо, и в документации для BatchRuntimeException ничего не говорится об этом. Итак, вопрос был бы: это правильный способ сделать это?Конфигурация статуса весеннего пакетного задания

Еще одна вещь, о которой я подумал, - это иметь смысл ОТКАЗАТЬ работу, если запись не удалась, но я думаю, что это имеет смысл при использовании прецедента, который у меня есть, что-то вроде этого: чтение записей из базы данных с использованием потока читатель (который настраивает запрос для работы с базой данных), затем используйте создатель потока для отправки этих записей по электронной почте или http (извлекайте конфигурацию, где отправлять элементы в методе открытого потока). Если все будет успешным, обновите записи базы данных со статусом SENT, если во время doOpen/open/write возникнет ошибка, вызовите StepExecutionListener, чтобы отправить уведомление, в котором задание не выполнено. Это одна из причин, по которым мне нужно, чтобы статус работы был FAILED, так что StepExecutionListener (который проверяет ExitCode, который должен быть FAILED) выполняется правильно. Вторая причина заключается в том, что я хочу использовать приложение Spring Batch Admin для управления заданиями, и если задание отображается как ЗАВЕРШЕНО, хотя запись не удалась, это кажется ошибочным, поскольку вся точка задания заключается в отправке элементов. Таким образом, задание может быть перезапущено, если оно не удалось, после изменения конфигурации (например, правильно настроен адрес электронной почты).

Кроме того, если вызовы записи не выполняются, то запись в базе данных должна обновляться до FAILED, которую я планирую сделать в onWriteError ItemWriteListener внутри новой транзакции, поскольку текущий откат.

Я разместил все это длинное описание, чтобы быть уверенным, что я не работаю против намерения структуры здесь, пытаясь установить статус работы FAILED от автора.

Ждем ваших размышлений об этом.

Привет, Cristi

PS: Работа настраивается так:

<batch:job id="job"> 
    <batch:step id="step"> 
     <batch:tasklet> 
      <batch:chunk reader="reader" writer="writer" reader-transactional-queue="true" commit-interval="#{properties['export.page.size']}"/> 
     </batch:tasklet> 
     <batch:listeners> 
      <batch:listener ref="failedStepListener"/> 
     </batch:listeners> 
    </batch:step> 
</batch:job> 

СПУСТЯ EDIT: Конфигурации читателя и писателя ниже:

<bean name="reader" class="...LeadsReader" scope="step"> 
    <property name="campaignId" value="#{jobParameters[campaignId]}" /> 
    <property name="partnerId" value="#{jobParameters[partnerId]}" /> 
    <property name="from" value="#{jobParameters[from]}" /> 
    <property name="to" value="#{jobParameters[to]}" /> 
    <property name="saveState" value="false" /> <!-- we use a database flag to indicate processed records --> 
</bean> 

<bean name="writer" class="...LeadsItemWriter" scope="step"> 
    <property name="campaignId" value="#{jobParameters[campaignId]}" /> 
</bean> 

Код для это:

public class LeadsItemWriter extends AbstractItemStreamItemWriter<Object[]> { 

//fields and setters omitted 

public LeadsItemWriter() { 
    setName(ClassUtils.getShortName(LeadsItemWriter.class)); 
} 

@Override 
public void open(ExecutionContext executionContext) { 
    super.open(executionContext); 
    PartnerCommunicationDTO partnerCommunicationDTO = this.leadableService.getByCampaignId(this.campaignId) 
      .getPartnerCommDTO(); 
    this.transportConfig = partnerCommunicationDTO != null ? partnerCommunicationDTO.getTransportConfig() : null; 
    this.encoding = partnerCommunicationDTO != null ? partnerCommunicationDTO.getEnconding() : null; 
    if (this.transportConfig == null) { 
     throw new ItemStreamException ("Failed to retrieve the transport configuration for campaign id: " 
       + this.campaignId); 
    } 
    PageRequestDTO pageRequestDTO = this.pageRequestMapper.map(partnerCommunicationDTO); 
    if (pageRequestDTO == null) { 
     throw new ItemStreamException("Wrong transport mapping configured for campaign id: " + this.campaignId); 
    } 
    this.columnNames = new ArrayList<>(); 
    for (LeadColumnDTO leadColumnDTO : pageRequestDTO.getColumns()) { 
     this.columnNames.add(leadColumnDTO.getName()); 
    } 
} 

@Override 
public void write(List<? extends Object[]> items) throws Exception { 
    try { 
     if (this.transportConfig.getTransportType() == TransportConfigEnum.EMAIL) { 
      this.leadExporterService.sendLeads(items, this.columnNames, this.transportConfig, this.encoding); 
     } else { 
      for (Object[] lead : items) { 
       this.leadExporterService.sendLead(lead, this.columnNames, this.transportConfig, this.encoding); 
      } 
     } 
    } catch (RuntimeException e) { 
     LOGGER.error("Encountered exception while sending leads.", e); 
     //wrap exception so that the job fails and the notification listener gets called 
     throw new BatchRuntimeException(e); 
    } 

} 

}

Код для читателя:

public class LeadsReader extends AbstractPagingItemReader<Object[]> { 

//fields and setters omitted 

public LeadsReader() { 
    setName(ClassUtils.getShortName(LeadsReader.class)); 
} 

@Override 
protected void doOpen() throws Exception { 
    this.pageRequestDTO = this.pageRequestMapper.map(this.leadableService.getByCampaignId(this.campaignId) 
      .getPartnerCommDTO()); 
    if (pageRequestDTO == null) { 
     throw new ItemStreamException("Wrong transport mapping configured for campaign id: " + this.campaignId); 
    } 
    this.timeInterval = new LeadTimeIntervalDTO(this.from != null ? of(this.from, 
      LeadQueryFilterParam.Comparison.GT) : null, 
      this.to != null ? of(this.to, LeadQueryFilterParam.Comparison.LE) : null); 

    super.doOpen(); 
} 

private LeadFilterDTO of(Date date, LeadQueryFilterParam.Comparison comparison) { 
    LeadFilterDTO filterDTO = new LeadFilterDTO(); 
    filterDTO.setColumn(CREATION_DATE); 
    filterDTO.setSqlType(DATE); 
    filterDTO.setComparison(comparison.name()); 
    filterDTO.setValue(DateUtil.format(date, Validator.DATE_FORMAT)); 
    return filterDTO; 
} 

@Override 
protected void doReadPage() { 
    if (results == null) { 
     results = new CopyOnWriteArrayList<>(); 
    } else { 
     results.clear(); 
    } 
    if (this.pageRequestDTO != null) { 
     results.addAll(LeadsReader.this.leadStorageService.listLeads(
       LeadsReader.this.pageRequestDTO.getColumns(), 
       LeadsReader.this.getFilters(), 
       LeadsReader.this.pageRequestDTO.getQueryOrderByParams(), 
       LeadsReader.this.pageRequestDTO.isUniqueByEmail(), LeadsReader.this.timeInterval, 
       (long) getPage() + 1, (long) getPageSize()).getExportedLeadsRows()); 
    } 

} 

private List<LeadFilterDTO> getFilters() { 

    List<LeadFilterDTO> filtersList = new ArrayList<>(); 

    LeadFilterDTO campaignFilter = new LeadFilterDTO(); 
    campaignFilter.setColumn(CAMPAIGN_ID); 
    campaignFilter.setValue(Long.toString(campaignId)); 
    campaignFilter.setSqlType(BIGINTEGER); 
    filtersList.add(campaignFilter); 

    LeadFilterDTO partnerFilter = new LeadFilterDTO(); 
    partnerFilter.setColumn(PARTNER_ID); 
    partnerFilter.setValue(Long.toString(partnerId)); 
    partnerFilter.setSqlType(BIGINTEGER); 
    filtersList.add(partnerFilter); 

    LeadFilterDTO statusFilter = new LeadFilterDTO(); 
    statusFilter.setColumn(STATUS); 
    statusFilter.setValue("VALID"); 
    statusFilter.setSqlType(CHAR); 

    filtersList.add(statusFilter); 

    return filtersList; 
} 

@Override 
protected void doJumpToPage(int itemIndex) { 
} 

}

ответ

0

Если исключение выбрасывается компонентом в рамках (в частности ItemReader, ItemProcessor, ItemWriter или Tasklet) и не пойман, шаг, выполняющий компонент, будет отмечен как сбой, если вы ничего не сделаете.Если шаг выходит из строя, задание также помечено как неудачное (это позволяет перезапустить его).

Короче говоря, вам не нужно делать ничего лишнего, чтобы заставить работу терпеть неудачу при вызове исключения.

+0

Это не совсем верно для всех случаев: например, если считыватель настроен с помощью reader-transactional-queue = "true", то StepParserStepFactoryBean вызывает метод createFaultTolerantStep, который, в свою очередь, настраивает RepeatTemplate с помощью SimpleRetryExceptionHandler (в createRetryOperations ()), который проглатывает исключение. –

+0

Можете ли вы поделиться конфигурацией для вашего читателя и писателя? –

+0

Конечно, я добавил конфигурацию для них в описании. –

Смежные вопросы