Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 31 additions & 20 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,17 @@
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>

<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
</dependencies>

<build>
Expand Down Expand Up @@ -133,26 +144,26 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<failsOnError>true</failsOnError>
<configLocation>src/main/resources/checkstyle.xml</configLocation>
<consoleOutput>true</consoleOutput>
</configuration>
<executions>
<execution>
<id>validate</id>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>

</plugin>
<!-- <plugin>-->
<!-- <groupId>org.apache.maven.plugins</groupId>-->
<!-- <artifactId>maven-checkstyle-plugin</artifactId>-->
<!-- <version>3.0.0</version>-->
<!-- <configuration>-->
<!-- <failsOnError>true</failsOnError>-->
<!-- <configLocation>src/main/resources/checkstyle.xml</configLocation>-->
<!-- <consoleOutput>true</consoleOutput>-->
<!-- </configuration>-->
<!-- <executions>-->
<!-- <execution>-->
<!-- <id>validate</id>-->
<!-- <phase>validate</phase>-->
<!-- <goals>-->
<!-- <goal>check</goal>-->
<!-- </goals>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- -->
<!-- </plugin>-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package somesh.github.io.fileconsumer.app.processor;

import lombok.Getter;
import somesh.github.io.fileconsumer.domain.model.FileUploadedStatus;

@Getter
public class FileDto {

private Long id;
private String fileReferenceId;
private String fileTypeCode;
private String fileName;
private String charSet;
private String submitterEmail;
private String fileContent;
private FileUploadedStatus status;
private String correlationId;
private String fileExtension;
private String fieldSeparator;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package somesh.github.io.fileconsumer.app.processor;

import lombok.Getter;

@Getter
public class InvalidFileContentException extends RuntimeException{

//public InvalidFileContentException(final List<FileEle>)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package somesh.github.io.fileconsumer.app.processor;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.sl.usermodel.Sheet;
import org.apache.poi.ss.usermodel.*;
import org.springframework.stereotype.Component;
import somesh.github.io.fileconsumer.app.shared.ExcelRow;

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Slf4j
@Component
public class SheetParser<EXCEL_ROW> {

private static final Integer HEADER_INDEX = 0;

private final DataFormatter cellFormat;
private final ObjectMapper mapper;

/**
*
* @param mapper
*/
public SheetParser(ObjectMapper mapper){
this.mapper = mapper;
cellFormat=new DataFormatter();
}

/**
*
* @param sheet
* @param clazz
* @return
*/
public List<EXCEL_ROW> parseFile(Sheet sheet, Class<EXCEL_ROW> clazz){

Iterator<Row> rowIterator = sheet.iterator();
List<String> rowHeaders = new ArrayList<>();
List<EXCEL_ROW> dtos = new ArrayList<>();

while (rowIterator.hasNext()){
processXlsRow(rowIterator.next(), rowHeaders).ifPresent(values ->{
try {
dtos.add(mapper.convertValue(values,clazz));
}catch (IllegalArgumentException e){
log.error("Failed to parse line.");

//publish FileElementValidationFailed DomainEvent
}
});
}

return dtos;
}

/**
*
* @param row
* @param headers
* @return
*/
private Optional<Map<String,String>> processXlsRow(Row row, List<String> headers){
Iterator<Cell> cellIterator = row.cellIterator();
Map<String,String> cellValues = new LinkedHashMap<>();

while (cellIterator.hasNext()){
processXlsCell(cellIterator.next(),headers,cellValues);
}

if (cellValues.size() > 0 && headers.size() >= cellValues.size()){
cellValues.put(ExcelRow.ROW_NUM_KEY, Integer.toString(row.getRowNum()+1));
cellValues.put(ExcelRow.SHEET_NAME_KEY, row.getSheet().getSheetName());
return Optional.of(cellValues);
}

return Optional.empty();
}

/**
*
* @param cell
* @param headers
* @param values
*/
private void processXlsCell(Cell cell, List<String> headers, Map<String,String> values) {
if (cell.getRow().getRowNum() == HEADER_INDEX){
headers.add(cell.getStringCellValue());
}else{

if(cell.getColumnIndex()>= headers.size()|| cell.getCellType()== CellType.BLANK){
return;
}
if (cell.getCellType() == CellType.NUMERIC && DateUtil.isCellDateFormatted(cell)){
ZonedDateTime zonedDateTime
= ZonedDateTime.ofInstant(cell.getDateCellValue().toInstant(), ZoneId.systemDefault());
values.put(headers.get(cell.getColumnIndex()), DateTimeFormatter.ISO_INSTANT.format(zonedDateTime.withZoneSameLocal(ZoneId.of("UTC"))));
} else{
values.put(headers.get(cell.getColumnIndex()),cellFormat.formatCellValue(cell));
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package somesh.github.io.fileconsumer.app.processor;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import somesh.github.io.fileconsumer.app.service.FileReceivedDomainEvent;
import somesh.github.io.fileconsumer.app.shared.SheetHandler;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Optional;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

@Component
@Slf4j
public class XmlProcessor {

@Autowired
private Collection<SheetHandler> sheetHandlers;

/**
* Handling of Domain Event
*
* @param fileReceivedDomainEvent FileReceivedDomainEvent
*/
@Async
@EventListener
public void handleFileRecievedDomainEvent(FileReceivedDomainEvent fileReceivedDomainEvent) {
log.info("Handling FileReceived Event ");
processFile(fileReceivedDomainEvent.getExecId(),fileReceivedDomainEvent.getFile());
}



/**
*
* @param execId
* @param fileDto
*/
public void processFile(final long execId, final FileDto fileDto) {
InputStream stream = new ByteArrayInputStream(Base64.decodeBase64(fileDto.getFileContent()));
String processingMessage;
try(Workbook workbook = new XSSFWorkbook(stream)){

final Stream<Sheet> sheets = StreamSupport.stream(workbook.spliterator(), false);
final long errorCount = sheets.mapToLong(sheet->processSheet(sheet,fileDto)).sum();

processingMessage = (errorCount == 0)? StringUtils.EMPTY : String.format("%d errors found on Excel file.",errorCount);

}catch (IOException | NotOfficeXmlFileException e){
processingMessage = MessageFormat.format("Can't open excel workbook {}",stream);
log.error(processingMessage);
}

//To-do publish FileProcessed MessageEvent
}

private long processSheet(final Sheet sheet, FileDto dto){
final Optional<SheetHandler> handler = sheetHandlers.stream().filter(h-> h.canHandle(sheet)).findAny();
long errorCount = 0L;
try {
if (handler.isPresent()){
log.info("Processing Excel Sheet '{}'.",sheet.getSheetName()) ;
} else {
//publish SheetNameNotSupportedDomain EVent.
}
} catch(InvalidFileContentException e){
errorCount = e.getMessage().split(",").length;
log.error("{} error occuered when processing excel sheet '{}' ",errorCount,sheet.getSheetName());
}
return errorCount;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package somesh.github.io.fileconsumer.app.service;

import org.springframework.stereotype.Service;

@Service
public class ExecutionService {

public void createExecution(){
// save event to db
//publish FileUploadedExecCreatedDomainEvent
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package somesh.github.io.fileconsumer.app.service;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.context.ApplicationEvent;
import somesh.github.io.fileconsumer.app.processor.FileDto;
import somesh.github.io.fileconsumer.app.service.messaging.FileUploadedMessageEvent;

import java.time.Instant;

/**
*
*/
@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class FileReceivedDomainEvent extends ApplicationEvent {

private long execId;
private FileDto file;
private String fileRefId;
private Instant occuredOn = Instant.now();


public FileReceivedDomainEvent(Object source) {
super(source);
}

}
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package somesh.github.io.fileconsumer.app.service.messaging;
package somesh.github.io.fileconsumer.app.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import somesh.github.io.fileconsumer.app.service.messaging.FileStatusUpdatedMessageEvent;
import somesh.github.io.fileconsumer.app.service.messaging.FileStatusUpdatedMessagePublisher;
import somesh.github.io.fileconsumer.app.service.messaging.FileUploadedMessageEvent;
import somesh.github.io.fileconsumer.domain.model.FileUploadedStatus;

@Component
public class FileReceivedDomainEventListener {

@Autowired
private FileStatusMessagePublisher fileStatusMessagePublisher;
private FileStatusUpdatedMessagePublisher fileStatusMessagePublisher;

/**
*
Expand All @@ -19,7 +22,7 @@ public class FileReceivedDomainEventListener {
@Async
@EventListener
public void doPublishOnKafka(FileReceivedDomainEvent domainEvent) {
FileStatusMessageEvent messageEvent = createFileStatusMessageEvent(domainEvent.getMesageEvent());
FileStatusUpdatedMessageEvent messageEvent = createFileStatusMessageEvent(domainEvent.getMesageEvent());
fileStatusMessagePublisher.publish(messageEvent);
}

Expand All @@ -28,8 +31,8 @@ public void doPublishOnKafka(FileReceivedDomainEvent domainEvent) {
* @param event FileUploadedMesageEvent
* @return FileStatusMessageEvent
*/
private FileStatusMessageEvent createFileStatusMessageEvent(FileUploadedMesageEvent event) {
return FileStatusMessageEvent.builder().eventDate(event.getEventDate())
private FileStatusUpdatedMessageEvent createFileStatusMessageEvent(FileUploadedMessageEvent event) {
return FileStatusUpdatedMessageEvent.builder().eventDate(event.getEventDate())
.fileName(event.getFileName()).fileLocation(event.getFileLocation())
.fileTypeCode(event.getFileTypeCode()).status(FileUploadedStatus.IN_PROGRESS).build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package somesh.github.io.fileconsumer.app.service;

import org.springframework.stereotype.Component;
import somesh.github.io.fileconsumer.app.service.messaging.FileUploadedMessageEvent;
import somesh.github.io.fileconsumer.app.shared.MessageEventHandler;

@Component
public class FileUploadHandler implements MessageEventHandler<FileUploadedMessageEvent> {

@Override public boolean canHandle(FileUploadedMessageEvent event) {
return false;
}

@Override public void handleEvent(FileUploadedMessageEvent event) {
//call ExecutionService execute()
}
}
Loading