We have a JSF application that gives us NotSerializableException sometimes without us knowing which object presents the problem. The Stacktrace gives little clue since it states that "Object" is the type that cannot be serialized.
I'd like to find the different approaches to locating the field.
We have solved the issue with the solution I posted, but I'd like to a) share it and b) see if this can be improved upon.
We created the following class and adding it as a listener to web.xml. This incurres a performance hit, so don't forget to remove it again if performance is an issue. This caught most of our serialization issues except for serialization of a servlet on the cluster.
public class SessionAttributeTracker implements HttpSessionAttributeListener
{
private static final Logger logger = LoggerFactory.getLogger(SessionAttributeTracker.class);
#Override
public void attributeAdded(final HttpSessionBindingEvent sessionBindingEvent)
{
Object obj = sessionBindingEvent.getValue();
logger.debug("Session attribute added: {}", obj);
if (!isSerializable(obj))
{
logger.warn("Attribute '{}' added to session with non-serializable object: {}",
sessionBindingEvent.getName(),
sessionBindingEvent.getValue());
}
}
private boolean isSerializable(final Object obj)
{
logger.debug("Checking serializability of : {}", obj.getClass().getName());
boolean ret = false;
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = null;
try
{
oos = new ObjectOutputStream(out);
oos.writeObject(obj);
ret = true;
}
catch (Exception e)
{
for (Field f : obj.getClass().getDeclaredFields())
{
logger.trace("obj {} felt {}", obj, f.getName());
if ((f.getModifiers() & Modifier.TRANSIENT) == 0)
{
f.setAccessible(true);
try
{
Object object = f.get(obj);
oos = new ObjectOutputStream(out);
// logger.debug("Hopper over writeObject");
oos.writeObject(object);
}
catch (Exception e1)
{
logger.debug("Problem encountered while serializing attribute {}", f.getName(), e1);
}
}
}
logger.warn("Serilization problem.", e);
return ret;
}
finally
{
IOUtils.closeQuietly(oos);
}
return ret;
}
private String threadDump()
{
StringBuffer fullThreadDump = new StringBuffer();
Thread t = Thread.currentThread();
State state = t.getState();
String tName = t.getName();
if (state != null)
{
fullThreadDump.append(" ").append(tName).append(": ").append(state).append("\n");
}
StackTraceElement[] stes = t.getStackTrace();
for (StackTraceElement stackTraceElement : stes)
{
fullThreadDump.append(" at ").append(stackTraceElement).append("\n");
}
return fullThreadDump.toString();
}
#Override
public void attributeRemoved(final HttpSessionBindingEvent sessionBindingEvent)
{
}
#Override
public void attributeReplaced(final HttpSessionBindingEvent sessionBindingEvent)
{
}
}
Related
Context
I made a Java application, and need to run two instances of that application, synchronizing some of their attributes via socket each time there's some change. To communicate those changes, Serializable objects are sent through a socket using ObjectStreams (input and output) using read/writeUTF() for an identifier, and read/writeObject() and flush(). The app is the exact same .jar, run twice with some changes like having different ports and ip (if necessary).
Problem
I noticed that objects of some of my classes (e.g. Notification) were sent and received without any troubles, but objects from another class (RegisteredUsers) weren't sent (or received) properly. So I ran some tests to send objects between the two apps and found that the object is being sent and isn't null, it's attribute (a HashMap<String,User>) is also being sent and isn't null, but is always empty.
So I decided to scale it down to what the problem was exactly: I'm trying to write an object through a Stream, and read it in a different process of the same .jar, and with most classes it seems to work, but it doesn't with one.
There seems to be something I'm missing or don't understand about this serialization process, if the object is written and read during the execution of the same process it works, but not if this object is read on another instance of the same app. I even added a HashMap to Notification with the same creation process, but it still works, I really don't get it, what am I missing?
Code
I have taken some code from the bigger app and trimmed it down to the basic problem if anyone wants to test it. To reproduce the errors, run Main1, which will create the two files with an object persisted in each one (one with a Notification object and the other with a RegisteredUsers object) and shows their information, then Main2, which reads them from the files and shows their information, and the problem should be printed. That being that reg3's HashMap is empty and thus neither of the Users are registered.
Main1
public class Main1 {
public static void main(String[] args) {
String regFile = "registry.txt";
String notificationFile = "notification.txt";
Persistence pers = new Persistence();
RegisteredUsers reg1 = new RegisteredUsers();
RegisteredUsers reg2 = new RegisteredUsers();
reg1.register("Name1", "127.0.0.1");
reg1.register("Name2", "127.0.0.1");
try {
pers.writeReg(reg1, regFile);
} catch (IOException e) {
System.out.println("Error writing registry.");
}
try {
reg2 = pers.readReg(regFile);
} catch (IOException e) {
System.out.println("Error reading registry.");
}
System.out.println("Original registry: ");
System.out.println(reg1.isRegistered("Name1") + " " + reg1.isRegistered("Name2"));
System.out.println("Registry read from file: ");
System.out.println(reg2.isRegistered("Name1") + " " + reg2.isRegistered("Name2"));
Notification noti1 = new Notification("Name", "127.0.0.1");
Notification noti2 = new Notification(); //not necesary but it's the way it's done in the bigger app.
try {
pers.writeNotif(noti1, notificationFile);
} catch (IOException e) {
System.out.println("Error writing notification.");
}
try {
noti2 = pers.readNotif(notificationFile);
} catch (IOException e) {
System.out.println("Error reading notification.");
}
System.out.println("Original notification: ");
System.out.println(noti1.getAttributes().get(0) + " " + noti1.getAttributes().get(1));
System.out.println(noti1.getMap());
System.out.println("Notification read from file: ");
System.out.println(noti2.getAttributes().get(0) + " " + noti2.getAttributes().get(1));
System.out.println(noti2.getMap());
}
}
Main2
public class Main2 {
public static void main(String[] args) {
String regFile = "registry.txt";
String notificationFile = "notification.txt";
Persistence pers = new Persistence();
RegisteredUsers reg3 = new RegisteredUsers();
try {
reg3 = pers.readReg(regFile);
} catch (IOException e) {
System.out.println("Error reading registry.");
}
if (reg3 == null) {
System.out.println("reg3 is null");
}
if (reg3.getMap() == null)
System.out.println("reg3 has a null map");
if (reg3.getMap().isEmpty())
System.out.println("reg3 has an empty map");
System.out.println("Registry read from file on another process: ");
System.out.println(reg3.isRegistered("Name1") + " " + reg3.isRegistered("Name2"));
Notification noti3 = new Notification(); //not necesary but it's the way it's done in the bigger app.
try {
noti3 = pers.readNotif(notificationFile);
} catch (IOException e) {
System.out.println("Error reading notification.");
}
System.out.println("Notification read from file on another process: ");
System.out.println(noti3.getAttributes().get(0) + " " + noti3.getAttributes().get(1));
System.out.println(noti3.getMap());
}
}
A Class to persist the objects in the files:
public class Persistence {
public void writeReg(RegisteredUsers regus, String file) throws IOException {
try(FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);) {
oos.writeObject(regus);
oos.flush();
}
}
public RegisteredUsers readReg(String file) throws IOException {
RegisteredUsers regus = null;
try(FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);) {
regus = (RegisteredUsers) ois.readObject();
} catch (ClassNotFoundException e) {
System.out.println("Wrong class.");
}
return regus;
}
public void writeNotif(Notification regus, String file) throws IOException {
try(FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);) {
oos.writeObject(regus);
oos.flush();
}
}
public Notification readNotif(String file) throws IOException {
Notification notif = null;
try(FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);) {
notif = (Notification) ois.readObject();
} catch (ClassNotFoundException e) {
System.out.println("Wrong class.");
}
return notif;
}
}
RegisteredUsers
public class RegisteredUsers implements Serializable {
private static HashMap<String, User> users;
public RegisteredUsers() {
users = new HashMap<String, User>();
}
public HashMap<String, User> getMap() {
return users;
}
public boolean isRegistered(String name) {
User us = users.get(name);
return us != null;
}
public void register(String name, String ip) {
users.put(name, new User(name, ip, false));
}
}
Notification
public class Notification implements Serializable {
private ArrayList<String> attributes;
private HashMap<String, User> map = new HashMap<>();
public Notification() {
}
public Notification(String name, String ip) {
attributes = new ArrayList<String>();
attributes.add(0, name);
attributes.add(1, ip);
map.put(ip, new User(name, ip, false));
}
public ArrayList<String> getAttributes() {
return attributes;
}
public HashMap<String, User> getMap() {
return map;
}
}
User
public class User implements Serializable {
private String name;
private String ip;
private boolean connection_state;
public User(String name, String ip, boolean connection_state) {
this.name = name;
this.ip = ip;
this.connection_state = connection_state;
}
}
In java static fields are implicitly transient, and transient fields are not serialized.
If you modify the RegisterdUsers to
public class RegisteredUsers implements Serializable {
private HashMap<String, User> users; // static modifier is removed
...
}
The serialization will work.
I am writing code to unmarshal XML from a file. I don't know up front which schema the XML is based on so I try to unmarshal it with several schemas in the form of different Jaxb2Marshaller instances.
The method needs to:
attempt to unmarshal the XML with each marshaller
If this succeeds, return the resulting object
If it fails, try the next marshaller
If all marshallers fail, throw an exception with the last error message
Here is the current code:
private Object getObject(final byte[] data) throws MyException {
String lastErrorMessage = "";
for (final Jaxb2Marshaller marshaller : this.marshallers) {
try {
return marshaller.unmarshal(new StreamSource(new ByteArrayInputStream(data)));
} catch (final XmlMappingException e) {
LOGGER.warn("Invalid XML", e);
lastErrorMessage = e.getMessage();
}
}
throw new MyException(lastErrorMessage);
}
I feel this method does too many things at different levels of abstraction:
iterate over marshallers
apply a marshaller
return result
catch exceptions
throw exception
But I don't see a way to simplify it. The try-catch block is needed for every marshaller (because I should catch and ignore these XmlMappingExceptions except the last one). That block either returns a result object, or the lastErrorMessage, which is needed below the iteration to throw the MyException.
The only solution I can think of is to create some contrived Result class which contains either the result object or the error message but that feels cludgy. Any other insights?
I would like methods with a granularity like these:
private Object getObject(byte[] data) throws MyException {
Result result;
for (Jaxb2Marshaller marshaller : this.marshallers) {
result = getObject(marshaller, data);
}
return handleError(result);
}
private Result getObject(Jaxb2Marshaller marshaller, byte[] data) {
try {
return Result.value(marshaller.unmarshal(new StreamSource(new ByteArrayInputStream(data))));
} catch (final XmlMappingException e) {
LOGGER.warn("Invalid XML", e);
return Result.error(e.getMessage());
}
}
private Object handleError(Result result) {
if (result.isError()) {
throw new MyException(result.errroMessage);
}
else {
return result.value;
}
}
But the additional Result class is verbose and cludgy:
private class Result {
String errorMessage;
Object value;
static Result error(String errorMessage) {
Result result = new Result();
result.errorMessage = errorMessage;
return result;
}
static Result value(Object value) {
Result result = new Result();
result.value = value;
return result;
}
boolean isError() {
return errorMessage != null;
}
}
How about this?
public class MultiUnmarshaller {
private final List<Jaxb2Marshaller> marshallers;
private Object value;
private String error;
public MultiUnmarshaller(List<Jaxb2Marshaller> marshallers) {
this.marshallers = marshallers;
}
private void init() {
error = "No marshallers available";
value = null;
}
public Object getObject(byte[] data) throws MyException {
init();
Iterator<Jaxb2Marshaller> it = marshallers.iterator();
while(it.hasNext() && errorMessage != null) {
unmarshalObject(marshaller, data);
}
return produceResult();
}
private void unmarshalObject(Jaxb2Marshaller marshaller, byte[] data) {
try {
value = marshaller.unmarshal(new StreamSource(new ByteArrayInputStream(data)));
error = null;
} catch (final XmlMappingException e) {
LOGGER.warn("Invalid XML", e);
error = e.getMessage();
}
}
private Object produceResult() {
if (error == null) {
return value;
}
else {
throw new MyException(error);
}
}
}
My app is saving a hashmap before it stops and when it starts again loads the same hashmap so changes could be made to it. I am using Serialization.
Storage class:
public class Storage {
private Map<String, String> storage;
private String projectStorageFilePath;
public Storage() {
this.storage = new ConcurrentHashMap<String, String>();
makeDir();
}
/**
* If the file in which the map objects will be saved doesn't exist in the
* user home directory it creates it.
*/
private void makeDir() {
File projectHomeDir = new File(System.getProperty("user.home"), ".TestMap");
String projectHomeDirPath = projectHomeDir.getAbsolutePath();
File projectStorageFile = new File(projectHomeDirPath, "storage.save");
projectStorageFilePath = projectStorageFile.getAbsolutePath();
if (!projectHomeDir.exists()) {
projectHomeDir.mkdir();
try {
projectStorageFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
#SuppressWarnings("unchecked")
public boolean load() {
boolean isLoaded = false;
ObjectInputStream ois = null;
try {
File file = new File(projectStorageFilePath);
if (file.length() != 0) {
//loading the map
ois = new ObjectInputStream(new FileInputStream(file));
storage = (ConcurrentHashMap<String, String>) ois.readObject();
isLoaded = true;
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (null != ois) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return isLoaded;
}
public boolean save() {
boolean isSaved = false;
ObjectOutputStream oos = null;
try {
//saving
oos = new ObjectOutputStream(new FileOutputStream(projectStorageFilePath));
oos.writeObject(storage);
isSaved = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != oos) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return isSaved;
}
public Map<String, String> getStorage() {
return this.storage;
}
}
The class in which I am trying to do something with that hashmap:
public class DoSomethingWithMap {
private Map<String, String> storage;
public DoSomethingWithMap(Map<String, String> storage) {
this.storage = storage;
}
public void addToMap(String key, String value) {
this.storage.put(key, value);
}
public void printMap() {
System.out.println(this.storage);
}
}
When I run it the first time it works fine:
public class Main {
public static void main(String[] args) {
Storage s = new Storage();
DoSomethingWithMap something = new DoSomethingWithMap(s.getStorage());
if (s.load()) {
System.out.println(s.getStorage());
}
something.addToMap("2", "test2");
something.addToMap("4", "test4");
something.addToMap("5", "test5");
if (s.save()) {
System.out.println(s.getStorage());
}
}
}
Output:
{} //empty map which is ok because it has never been saved before
{3=test3, 4=test4, 5=test5} //changes during runtime are saved
The problem is when I start Main again and try to make changes to the saved map:
public static void main(String[] args) {
Storage s = new Storage();
DoSomethingWithMap something = new DoSomethingWithMap(s.getStorage());
if (s.load()) {
System.out.println(s.getStorage());
}
something.printMap();
something.addToMap("6", "newTest");
something.addToMap("7", "newTest");
something.addToMap("8", "newTest");
something.printMap();
if (s.save()) {
System.out.println(s.getStorage());
}
}
Output:
{3=test3, 4=test4, 5=test5} //loading the map works fine
{} //here it should be same as previous line but is not
{6=newTest, 7=newTest, 8=newTest} //DoSomethingWithMap.printMap is printing only the changes during runtime
{3=test3, 4=test4, 5=test5} // changes during runtime are not saved
It is obvious DoSomethingWithMap class is not using the map which was given to it. Why? Which map is using? How I can fix that?
Thank you.
You are creating a new instance of the Map in your load method:
storage = (ConcurrentHashMap<String, String>) ois.readObject();
To fix you can clear the current map and then add all the values from the loaded one:
//loading the map
ois = new ObjectInputStream(new FileInputStream(file));
storage.clear();
storage.putAll((ConcurrentHashMap<String, String>) ois.readObject());
To prevent such error in the future, you could make those fields final and thus you will get error reports.
Spring has made it so incredibly easy to set up application properties...but how would you do it without Spring?
I need to deploy a Java / Groovy application to a server where using Spring is out of the question... and I also don't have the liberty to install anything like Redis either. One option I am considering is to set up a Spring Cloud Config Server elsewhere and have my application consume properties from the config server. Trouble is, that is a bit of an overkill for my project now.
Could anyone suggest a way to do this in good, old, plain Java? :)
This is a really simple and basic example, but you can modify it as you like:
PropertyConfigurator.java
public class PropertiesConfigurator
{
Properties properties = new Properties();
String configInputPath = null;
InputStream configInputStream = null;
public PropertiesConfigurator(String configInputPath)
{
this.configInputPath = configInputPath;
}
public PropertiesConfigurator load() throws IOException, PropertyException
{
try
{
this.configInputStream = new FileInputStream(this.configInputPath);
// load a properties file
this.properties.load(this.configInputStream);
validate();
}
catch (IOException ex)
{
System.out.println("Failed load properties file: " + this.configInputPath);
throw ex;
}
catch (PropertyException ex)
{
System.out.println("One or more properties are empty");
throw ex;
}
finally
{
if (this.configInputStream != null)
{
try
{
this.configInputStream.close();
}
catch (IOException ex)
{
System.out.println("Failed to close input stream");
throw ex;
}
}
}
return this;
}
private void validate() throws PropertyException
{
Enumeration<?> e = this.properties.propertyNames();
while (e.hasMoreElements())
{
String key = (String) e.nextElement();
String value = this.properties.getProperty(key);
if (value.isEmpty())
{
System.out.println(String.format("Property %s is empty!", key));
throw new PropertyException("One or more properties are empty");
}
}
}
public String getProperty(String key)
{
return this.properties.getProperty(key);
}
#Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (!(o instanceof PropertiesConfigurator))
return false;
PropertiesConfigurator that = (PropertiesConfigurator) o;
if (properties != null ? !properties.equals(that.properties) : that.properties != null)
return false;
if (configInputPath != null ? !configInputPath.equals(that.configInputPath) : that.configInputPath != null)
return false;
return configInputStream != null ?
configInputStream.equals(that.configInputStream) :
that.configInputStream == null;
}
#Override
public int hashCode()
{
int result = properties != null ? properties.hashCode() : 0;
result = 31 * result + (configInputPath != null ? configInputPath.hashCode() : 0);
result = 31 * result + (configInputStream != null ? configInputStream.hashCode() : 0);
return result;
}
}
PropertyException.java
public class PropertyException extends Exception
{
public PropertyException()
{
}
public PropertyException(String message)
{
super(message);
}
public PropertyException(String message, Throwable throwable)
{
super(message, throwable);
}
}
MainRunner.java
public class MainRunner
{
public static void main(String[] args)
{
try
{
String configFilePath = "application.properties";
PropertiesConfigurator propertiesConfigurator = new PropertiesConfigurator(configFilePath).load();
String prop1 = propertiesConfigurator.getProperty("keyprop1");
// Do whatever you want with prop1
// ...
}
catch (PropertyException ex)
{
System.out.println("Failed to load properties");
System.exit(1);
}
catch (Exception ex)
{
System.out.println("Error in main application");
System.exit(1);
}
}
}
Example of application.properties
keyprop1=value1
keyprop2=value2
Again, it's very basic and you should definitely improve this code and add your logic, validation, etc.
Take a look at http://constretto.org. That's easy to use configuration framework.
So i want to implement simple application which send notification kafka producer to kafka consumer.So far i have successfully send String message to producer to consumer.But when i try to send notification object kafka consumer didn't receive any objects.This is the code i have used.
public class Notification implements Serializable{
private String name;
private String message;
private long currentTimeStamp;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public long getCurrentTimeStamp() {
return currentTimeStamp;
}
public void setCurrentTimeStamp(long currentTimeStamp) {
this.currentTimeStamp = currentTimeStamp;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Notification that = (Notification) o;
if (currentTimeStamp != that.currentTimeStamp) return false;
if (message != null ? !message.equals(that.message) : that.message != null) return false;
if (name != null ? !name.equals(that.name) : that.name != null) return false;
return true;
}
#Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (message != null ? message.hashCode() : 0);
result = 31 * result + (int) (currentTimeStamp ^ (currentTimeStamp >>> 32));
return result;
}
#Override
public String toString() {
return "Notification{" +
"name='" + name + '\'' +
", message='" + message + '\'' +
", currentTimeStamp=" + currentTimeStamp +
'}';
}
}
And this is producer
public class KafkaProducer {
static String topic = "kafka-tutorial";
public static void main(String[] args) {
System.out.println("Start Kafka producer");
Properties properties = new Properties();
properties.put("metadata.broker.list", "localhost:9092");
properties.put("serializer.class", "dev.innova.kafka.tutorial.producer.CustomSerializer");
ProducerConfig producerConfig = new ProducerConfig(properties);
kafka.javaapi.producer.Producer<String, Notification> producer = new kafka.javaapi.producer.Producer<String, Notification>(producerConfig);
KeyedMessage<String, Notification> message = new KeyedMessage<String, Notification>(topic, createNotification());
System.out.println("send Message to broker");
producer.send(message);
producer.close();
}
private static Notification createNotification(){
Notification notification = new Notification();
notification.setMessage("Sample Message");
notification.setName("Sajith");
notification.setCurrentTimeStamp(System.currentTimeMillis());
return notification;
}
}
And this is consumer
public class KafkaConcumer extends Thread {
final static String clientId = "SimpleConsumerDemoClient";
final static String TOPIC = "kafka-tutorial";
ConsumerConnector consumerConnector;
public KafkaConcumer() {
Properties properties = new Properties();
properties.put("zookeeper.connect","localhost:2181");
properties.put("group.id","test-group");
properties.put("serializer.class", "dev.innova.kafka.tutorial.producer.CustomSerializer");
properties.put("zookeeper.session.timeout.ms", "400");
properties.put("zookeeper.sync.time.ms", "200");
properties.put("auto.commit.interval.ms", "1000");
ConsumerConfig consumerConfig = new ConsumerConfig(properties);
consumerConnector = Consumer.createJavaConsumerConnector(consumerConfig);
}
#Override
public void run() {
Map<String, Integer> topicCountMap = new HashMap<String, Integer>();
topicCountMap.put(TOPIC, new Integer(1));
Map<String, List<KafkaStream<byte[], byte[]>>> consumerMap = consumerConnector.createMessageStreams(topicCountMap);
KafkaStream<byte[], byte[]> stream = consumerMap.get(TOPIC).get(0);
ConsumerIterator<byte[], byte[]> it = stream.iterator();
System.out.println("It :" + it.size());
while(it.hasNext()){
System.out.println(new String(it.next().message()));
}
}
private static void printMessages(ByteBufferMessageSet messageSet) throws UnsupportedEncodingException {
for(MessageAndOffset messageAndOffset: messageSet) {
ByteBuffer payload = messageAndOffset.message().payload();
byte[] bytes = new byte[payload.limit()];
payload.get(bytes);
System.out.println(new String(bytes, "UTF-8"));
}
}
}
And finally i have used customserializer to serialize and deserialize object.
public class CustomSerializer implements Encoder<Notification>, Decoder<Notification> {
public CustomSerializer(VerifiableProperties verifiableProperties) {
/* This constructor must be present for successful compile. */
}
#Override
public byte[] toBytes(Notification o) {
return new byte[0];
}
#Override
public Notification fromBytes(byte[] bytes) {
return null;
}
}
Can someone tell me what is the issue ? is this the right way ?
You have two problems.
First, your deserializer doesn't have any logic. It returns an empty byte array for each object it serializes and returns a null object whenever it's asked to deserialize an object. You need to put code there that actually serializes and deserializes your objects.
Second, if you plan to use the native JVM serialization and deserialization logic from the JVM, you'll need to add a serialVersionUID to your beans that will be transported. Something like this:
private static final long serialVersionUID = 123L;
You can use any value you like. When an object is deserialized by the JVM the serialVersionId in the object is compared to the value specified in the loaded class definition. If the two are different then the JVM assumes that even though you have a class definition loaded you don't have the correct version of the class definition loaded and serialization will fail. If you don't specify a value for serialVersionID in your class definition then the JVM will make one up for you and two different JVM's (the one with the producer and the one with the consumer) will almost certainly make up different values for you.
EDIT
You'd need to make your serializer look something like this if you want to leverage the default Java serialization:
public class CustomSerializer implements Encoder<Notification>, Decoder<Notification> {
public CustomSerializer(VerifiableProperties verifiableProperties) {
/* This constructor must be present for successful compile. */
}
#Override
public byte[] toBytes(Notification o) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();
byte[] b = baos.toByteArray();
return b;
} catch (IOException e) {
return new byte[0];
}
}
#Override
public Notification fromBytes(byte[] bytes) {
try {
return (Notification) new ObjectInputStream(new ByteArrayInputStream(b)).readObject();
} catch (Exception e) {
return null;
}
}
Create a custom deserializer , Kafka need a way to serialize and deserialize .We have to provide both of these implementations so far
Need to add library to get the object mapper class
FasterXML jackson – 2.8.6
Example - serializer
public class PayloadSerializer implements org.apache.kafka.common.serialization.Serializer {
#Override
public byte[] serialize(String arg0, Object arg1) {
byte[] retVal = null;
ObjectMapper objectMapper = new ObjectMapper();
TestModel model =(TestModel) arg1;
try {
retVal = objectMapper.writeValueAsString(model).getBytes();
} catch (Exception e) {
e.printStackTrace();
}
return retVal;
}
#Override
public void close() {
}
#Override
public void configure(Map map, boolean bln) {
}
}
Deserializer
public class PayloadDeserializer implements Deserializer {
#Override
public void close() {
}
#Override
public TestModel deserialize(String arg0, byte[] arg1) {
ObjectMapper mapper = new ObjectMapper();
TestModel testModel = null;
try {
testModel = mapper.readValue(arg1, TestModel.class);
} catch (Exception e) {
e.printStackTrace();
}
return testModel;
}
#Override
public void configure(Map map, boolean bln) {
}
}
Finally we have to pass deserializer class to the receiver
ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG - PayloadDeserializer.class
or
deserializer.class - classpath.PayloadDeserializer
I strongly suggest you to convert your object to an Avro object before sending it.
It is not that difficult and is the Kafka way of transmitting objects.