Spring Batch - Get FileName inside a CustomLineMapper when using MultiResourceItemReader - java

I am using a Item with the inferface ResourceAware implemented to get the current resource filename. I get it in the ItemProcessor and this is working fine to log a complete information about the error.
Anyway, I want get the fileName if the moment of the FieldSetMapper throw an Exception, to register the line content, the line number and the file name but I don't found the correct way.
This is my current code:
public class MyCustomLineMapper<T> implements LineMapper<BaseDTO>, InitializingBean {
private LineTokenizer tokenizer;
private BeanWrapperFieldSetMapper<BaseDTO> fieldSetMapper;
public BaseDTO mapLine(String line, int lineNumber) throws Exception {
try {
BaseDTO r = fieldSetMapper.mapFieldSet(tokenizer.tokenize(line));
r.setLineNumber(lineNumber);
r.setLineContent(line);
return r;
} catch(Exception ex){
BaseDTO r = new BaseDTO();
r.setError(String.format(Keys.LINE_PARSE_ERROR, lineNumber, "FILE_NAME", line));
return r;
/// I want use the current filename in this moment.
}
}
public void setLineTokenizer(LineTokenizer tokenizer) {
this.tokenizer = tokenizer;
}
public void setFieldSetMapper(BeanWrapperFieldSetMapper<BaseDTO> fieldSetMapper) {
this.fieldSetMapper = fieldSetMapper;
}
public void afterPropertiesSet() {
Assert.notNull(tokenizer, "The LineTokenizer must be set");
Assert.notNull(fieldSetMapper, "The FieldSetMapper must be set");
}
}
In my item "BaseDTO" I implemented ResourceAware like this:
public class BaseDTO implements ResourceAware {
protected String itemClassifier;
protected String error;
protected int lineNumber;
protected String lineContent;
protected Resource res;
protected String fileName;
#Override
public void setResource(Resource res) {
this.res = res;
this.fileName = res.getFilename();
}
But in case of an exception reading a file (IncorrecNumberOfTokens by Example) I need get my item from FieldSetMapper with this field filled but the exception stop this. How can I achieve this?
Sorry for my english. Thanks in advance for all the help.

Related

Add Header and Footer for AvroIO Writer

We have a requirements to add a header and footer to a output avro file, but seems like the SDK doesn't support it. For TextIO writer, it seems to have that capability withHeader and withFooter.
That been said, what's the best way to do it without creating a separate pipeline? I tried add another step after the writer, but seems like the pipeline assumed to be ended after the writer.
Finally found a solution by extending FileBasedSink.
public class CustomAvroSink extens FileBasedSink{
...
#Override
public WriteOperation<DestinationT, GenericRecord> createWriteOperation() {
return new CustomAvroWriteOperation(this, this.genericRecords, this.header, this.footer);
....
}
private static class CustomAvroWriteOperation<DestinationT, OutputT> extends
WriteOperation<DestinationT, OutputT> {
private final DynamicAvroDestinations<?, DestinationT, OutputT> dynamicDestinations;
private final boolean genericRecords;
private final OutputT header;
private final OutputT footer;
private CustomAvroWriteOperation(HeaderFooterAvroSink<?, DestinationT, OutputT> sink,
boolean genericRecords, OutputT header, OutputT footer) {
super(sink);
this.dynamicDestinations = sink.getDynamicDestinations();
this.genericRecords = genericRecords;
this.header = header;
this.footer = footer;
}
public Writer<DestinationT, OutputT> createWriter() throws Exception {
return new CustomAvroWriter<>(this, this.dynamicDestinations, this.genericRecords,
this.header, this.footer);
}
}
...
private static class CustomAvroWriter<DestinationT, OutputT> extends
Writer<DestinationT, OutputT> {
#Override
protected void writeHeader() throws Exception {
if (this.header != null) {
this.dataFileWriter.append(this.header);
}
}
#Override
protected void writeFooter() throws Exception {
if (this.footer != null) {
this.dataFileWriter.append(this.footer);
}
}
}
}
Then I can just do myPCollection.apply("header footer",WriteFiles.to(new CustomAvroSink(...header, footer...)))

Expected BEGIN_ARRAY but was BEGIN_OBJECT in Retrofit

I am new to android programming and can anyone help me or point out why its giving me this error
I want to fetch some data from the server such as under the Hardware json and get the names and status, but when i call api its shows me this.
Change the line
public void onResponse(Call<List<ObjectList>> call, Response<List<ObjectList>> response) {
to
public void onResponse(Call<List<ObjectList>> call, Response<ObjectList> response) {
As per your code, you are expecting response as List. But Actual response is object. So, you need to generate model class based on your response and set in code for output.
Your Model should be like :
public class Application {
ArrayList<Object> hardware = new ArrayList<Object>();
Header HeaderObject;
ArrayList<Object> software = new ArrayList<Object>();
// Getter Methods
public Header getHeader() {
return HeaderObject;
}
// Setter Methods
public void setHeader( Header headerObject ) {
this.HeaderObject = headerObject;
}
}
public class Header {
Stamp StampObject;
private String frame_id;
private float seq;
// Getter Methods
public Stamp getStamp() {
return StampObject;
}
public String getFrame_id() {
return frame_id;
}
public float getSeq() {
return seq;
}
// Setter Methods
public void setStamp( Stamp stampObject ) {
this.StampObject = stampObject;
}
public void setFrame_id( String frame_id ) {
this.frame_id = frame_id;
}
public void setSeq( float seq ) {
this.seq = seq;
}
}
public class Stamp {
private float secs;
private float nsecs;
// Getter Methods
public float getSecs() {
return secs;
}
public float getNsecs() {
return nsecs;
}
// Setter Methods
public void setSecs( float secs ) {
this.secs = secs;
}
public void setNsecs( float nsecs ) {
this.nsecs = nsecs;
}
}
Then change below line :
public void onResponse(Call<List<ObjectList>> call, Response<Application> response) {
Change this:
#GET("system_monitor")
Call<List<ObjectList>> getHardware();
to
#GET("system_monitor")
Call<ObjectList> getHardware();
Your response return an object instead of array.
Instead of
#GET("system_monitor")
Call<List<ObjectList>> getHardware();
use
#GET("system_monitor")
Call<ObjectList> getHardware();
And then use it like below:
Call<ObjectList> call = webRequestAPI.getHardware();
call.enqueue(new Callback<ObjectList>() {
#Override
public void onResponse(Call<ObjectList> call, Response<ObjectList> response) {
if (!response.isSuccessful()) {
textViewHardwareName.setText("Code: " + response.code());
return;
}
ObjectList system_monitor = response.body();
...
}
#Override
public void onFailure(Call<ObjectList> call, Throwable t) {
textViewHardwareName.setText(t.getMessage());
}
});
The best thing for your scenario hardware and Software are as objects , which have two property
1.Name 2. Object status.
So I recommend you to create a class name as System and put there these two variables so finally your class looks like :
Class System
{
String object_name;
boolean object_status;
}
and your getter setter .
And update your model class like this
#SerializedName("hardware")
#Expose
public List<System> hardware;
#SerializedName("software")
#Expose
public List<System> software;
and change your retrofit response holder as.
public void onResponse(Call<List<ObjectList>> call, Response<ObjectList>
response) {

How to write custom runtime exception to send a json response with 2 custom information?

The context is:
I'm making a upload csv service and when i do some verification on the content i want to throw a custom runtime exception who contain the error message and the line to the problem.
What i got for now is:
public class NotValidCsvException extends RuntimeException {
private static final long serialVersionUID = 1L;
private Integer _line;
public NotValidCsvException(final String message, final Integer line) {
super(formatMessage(message, line));
}
public static String formatMessage(String message, Integer line) {
return new StringBuilder()
.append("{message: ")
.append("'")
.append(message)
.append("'")
.append(", ligne: ")
.append("'")
.append(line)
.append("'")
.append("}").toString();
}
}
But the problem is that i got a response like:
{"timestamp":1511785651810,"status":500,"error":"Internal Server Error","exception":"com.sstrn.pa.service.impl.exception.NotValidCsvException","message":"{"message": "message_import_csv_undefined_thing", "line": 0}"}
But i'd like to have something like:
{"timestamp":1511785651810,"status":500,"error":"Internal Server Error","exception":"com.sstrn.pa.service.impl.exception.NotValidCsvException","message":"message_import_csv_undefined_thing", "line": 0}
So to do this i created and interface AttributeLineEnabled whose implemented by my NotValidCsvException.
public interface AttributeLineEnabled {
public Integer getLine();
}
public class NotValidCsvException extends RuntimeException implements AttributeLineEnabled {
private static final long serialVersionUID = 1L;
private Integer _line;
public NotValidCsvException(final String message) {
super(message);
}
public NotValidCsvException(final String message, Integer line) {
super(message);
_line = line;
}
public Integer getLine() {
return _line;
}
public void setLine(final Integer line) {
_line = line;
}
}
Then i created a controller who extends DefaultErrorAttributes and implement ErrorAttributes.
This controller override the getErrorAttributes method who set the jsonResponse.
public class BaseErrorAttributes extends DefaultErrorAttributes implements ErrorAttributes {
private static final String LINE = "line";
public BaseErrorAttributes() {
super();
}
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
Map<String, Object> errorAttributes = super.getErrorAttributes(requestAttributes, includeStackTrace);
Throwable error = getError(requestAttributes);
if (error instanceof AttributeLineEnabled) {
AttributeLineEnabled attributeLineEnabled= (AttributeLineEnabled) error;
errorAttributes.put(LINE, attributeLineEnabled.getLine());
}
return errorAttributes;
}
}
And i inject it into my controller who make the importation process:
private BaseErrorAttributes _baseErrorAttributes;
// getter and setter too

Read Variable from a Class

Hi after countless hours, I figured out what I really what my problem is but still cannot find an answer.
#Override
public void onStampResult(StampResult result) {
}
onStampResult returns a Class StampResult with the follwowing parameters:
public class StampResult implements Serializable {
public SnowShoeError error;
public SnowShoeStamp stamp;
public boolean secure;
public String receipt;
public Date created;
}
SnowShoeStamp Class is:
public class SnowShoeStamp implements Serializable {
public String serial;
}
And SnowShoeError Class is:
public class SnowShoeError implements Serializable {
public int code;
public String message;
}
In onStampResult I can write down logic depending on the output of result.
On Success ´stamp´ gets initialized and ´error´ does not exist.
On Error, stamp does not exist and error gets initialized.
The result gets parsed to from JSON to the Class in the following way:
try {
stampResult = gson.fromJson(result, StampResult.class);
} catch (JsonSyntaxException jsonException) {
stampResult = new StampResult();
stampResult.error = new SnowShoeError();
stampResult.error.message = result;
}
mOnStampListener.onStampResult(stampResult);
mStampBeingChecked = false;
}
How do I test if either error or stamp exists without getting a NullPointerExeption?
Unless I misunderstood your question, you simply need to check for null.
In order to handle the different cases, you could do the following:
#Override
public void onStampResult(StampResult result) {
if (result.error == null){
SnowShoeStamp stamp = result.stamp;
// Process stamp
} else {
SnowShoeError error = stampResult.error;
// Process error
}
}

Use reflection to create classes at runtime

I have to create a list of objects, which are configured according to the name of some classes received as input.
For each object I have to call a method, which add an operation that is created dynamically.
However I don't know exactly ho to resolve the problem.
Please see an example below.
String className; // this is an input parameter
final Class<?> classType = Class.forName(className);
// here I would like to use classType instead of "?" but it gives me an error.
Task<?> task = TaskFactory.createTask((String)classType.getField("_TYPE").get(null)));
tasks.put(task, null);
task.addOperation(new Operation<classType>() { // this gives an error
#Override
public void onNewInput(classType input) { // this gives an error
System.out.println(input)
}
});
As you can see from the comments, the surrounding infrastructure and the intention are not entirely clear. However, you can achieve a certain degree of type-safety with a "helper" method that captures the type of the given Task, and allows you to work with this type internally:
public class RuntimeType
{
public static void main(String[] args) throws Exception
{
String className = "";
final Class<?> classType = Class.forName(className);
Task<?> task = TaskFactory.createTask((String)classType.getField("_TYPE").get(null));
addOperation(task);
}
private static <T> void addOperation(Task<T> task)
{
task.addOperation(new Operation<T>()
{
#Override
public void onNewInput(T input)
{
System.out.println(input);
}
});
}
}
class TaskFactory
{
public static Task<?> createTask(String string)
{
return null;
}
}
class Task<T>
{
public void addOperation(Operation<T> operation)
{
}
}
interface Operation<T>
{
void onNewInput(T input);
}

Categories