I have two consumers and each consumer has their own validation logic entirely different from one other so I am using two validator class for that, one for each other.
ConsumerA
, for this consumer, I am usingValidatorA
.ConsumerA
, for this consumer, I am usingValidatorB
.
ConsumerA class
public class ConsumerA extends Consumer {
.....
@Override
protected void run(String consumerName, Properties consumerProps) {
consumer = new KafkaConsumer<>(consumerProps);
consumer.subscribe(getTopicsBasisOnConsumerName());
try {
while (!closed.get()) {
ConsumerRecords<byte[], byte[]> records = consumer.poll(2000);
for (ConsumerRecord<byte[], byte[]> record : records) {
GenericRecord genericRecord = decoder.decode(record.value());
ValidatorA validation = new ValidatorA(consumerName, genericRecord);
if (!validation.isValid()) {
logger.logError("dropping records. payload= ", genericRecord);
continue;
}
// setting variables to DataPacket, do I need this class at all?
DataPacket packet = new DataPacket.Builder(genericRecord).setClientId(validation.getClientId())
.setDeviceId(validation.getDeviceId()).setPayId(validation.getPayId()).setHolder(validation.getHolder())
.setOldTimestamp(validation.getOldTimestamp()).setNewTimestamp(validation.getNewTimestamp()).build();
Processor.getInstance().execute(packet);
}
}
} catch (Exception ex) {
System.out.println("error= " + ex);
}
}
}
ConsumerB class
public class ConsumerB extends Consumer {
.....
@Override
protected void run(String consumerName, Properties consumerProps) {
consumer = new KafkaConsumer<>(consumerProps);
consumer.subscribe(getTopicsBasisOnConsumerName());
try {
while (!closed.get()) {
ConsumerRecords<byte[], byte[]> records = consumer.poll(2000);
for (ConsumerRecord<byte[], byte[]> record : records) {
GenericRecord genericRecord = decoder.decode(record.value());
ValidatorB validation = new ValidatorB(consumerName, genericRecord);
if (!validation.isValid()) {
logger.logError("dropping records. payload= ", genericRecord);
continue;
}
// setting variables to DataPacket, do I need this class at all?
DataPacket packet = new DataPacket.Builder(genericRecord).setType(validation.getType())
.setDatumId(validation.getDatumId()).setItemId(validation.getItemId()).setOldTimestamp(validation.getOldTimestamp())
.setNewTimestamp(validation.getNewTimestamp()).build();
Processor.getInstance().execute(packet);
}
}
} catch (Exception ex) {
System.out.println("error= " + ex);
}
}
}
Below are my corresponding validator classes:
ValidatorA class This class is called by ConsumerA
, basically it's isValid
method for validation.
public class ValidatorA extends Validator {
private static final Logger logger = Logger.getInstance(ValidatorA.class);
private final String consumerName;
private final GenericRecord genericRecord;
private final Long oldTimestamp;
private final Long newTimestamp;
private final String clientId;
private final String deviceId;
private final Integer payId;
private final Map<String, String> holder;
public ValidatorA(String consumerName, GenericRecord genericRecord) {
this.consumerName = consumerName;
this.genericRecord = genericRecord
this.oldTimestamp = (Long) DataUtils.parse(genericRecord, "oldTimestamp");
this.newTimestamp = (Long) DataUtils.parse(genericRecord, "newTimestamp");
this.clientId = (String) DataUtils.parse(genericRecord, "clientId");
this.deviceId = (String) DataUtils.parse(genericRecord, "deviceId");
this.payId = (Integer) DataUtils.parse(genericRecord, "payId");
this.holder = (Map<String, String>) DataUtils.parse(genericRecord, "holder");
}
@Override
public boolean isValid() {
return isValidClientIdDeviceId() && isValidPayId() && isValidHolder();
}
private boolean isValidHolder() {
if (MapUtils.isEmpty(holder)) {
logger.logError("invalid holder.");
return false;
}
return true;
}
private boolean isValidPayId() {
if (payId == null) {
logger.logError("invalid payId.");
return false;
}
return true;
}
private boolean isValidClientIdDeviceId() {
if (Strings.isNullOrEmpty(clientId) && Strings.isNullOrEmpty(deviceId)) {
logger.logError("invalid clientId and deviceId.");
return false;
}
return true;
}
// getter and toString method here
}
ValidatorB class This class is called by ConsumerB
, basically it's isValid
method for validation.
public class ValidatorB extends Validator {
private static final Logger logger = Logger.getInstance(ValidatorB.class);
private final String consumerName;
private final GenericRecord genericRecord;
private final Long oldTimestamp;
private final Long newTimestamp;
private final String type;
private final String datumId;
private final String itemId;
public ValidatorB(String consumerName, GenericRecord genericRecord) {
this.consumerName = consumerName;
this.genericRecord = genericRecord
this.oldTimestamp = (Long) DataUtils.parse(genericRecord, "oldTimestamp");
this.newTimestamp = (Long) DataUtils.parse(genericRecord, "newTimestamp");
this.type = (String) DataUtils.parse(genericRecord, "type");
this.datumId = (String) DataUtils.parse(genericRecord, "datumId");
this.itemId = (Integer) DataUtils.parse(genericRecord, "itemId");
}
@Override
public boolean isValid() {
return isValidType() && isValidDatumId() && isValidItemId();
}
private boolean isValidType() {
if (Strings.isNullOrEmpty(type)) {
logger.logError("invalid type is coming.");
return false;
}
return true;
}
private boolean isValidDatumId() {
if (Strings.isNullOrEmpty(datumId)) {
logger.logError("invalid datumId is coming.");
return false;
}
return true;
}
private boolean isValidItemId() {
if (Strings.isNullOrEmpty(itemId)) {
logger.logError("invalid itemId is coming.");
return false;
}
return true;
}
// getter and toString method here
}
Validator class
public abstract class Validator {
public abstract boolean isValid();
}
Now in my Processor
class in the execute
method, I am using DataPacket
passed from both the consumer to extract all these variables which have been set, do some processing by using those variables. And later on we will get some new variables which I am setting it by cloning the old packet
builder.
private void execute(DataPacket packet) {
// extract all the fields from this packet
// do some processing using them
// now clone the packet and set processed variables.
String type = packet.getType();
String clientId = packet.getClientId();
// do some processing
String schemaId = ....;
Schema schema = ....;
// cloning old packet and setting new variables
DataPacket clonedPacket = new DataPacket.Builder(packet).setSchemaId(schemaId).setSchema(schema).build();
// send clonedPacket to some other system which will send to database
}
My question is - Do I need this DataPacket
at all when I already have ValidatorA
, ValidatorB
class? DataPacket
class just contains all the variables combine from both the validator class and I am using DataPacket
builder class just to set data in both the consumers and then use it in execute method of Processor class
, and then clone the old builder to make a new builder object again by setting some new variables.
Is there any better way to do this thing? Looks like I can get rid of DataPacket
but not sure how.