I have a question regarding Singleton class behavior.
Scenario
We have webservices that is used by partners with request coming as frequently as 2-3 times in 1 second. So, they are heavily used services. We would like to store the request and response in JSON format.
As of now, in each webservice interface class, we have initiated Gson object and doing the serialization of java object to json.
Question
I was just thinking if I initiate Gson object once in Singleton class as static object and then refer it for all the request / response serialization of java object to Json, can it cause any issue/problem?
I mean, as there are multiple webservices and couple of them are heavily used by partners, so will single instance of GSON accessed in all the webservice can cause any delay or any other problem? If yes, apart than memory, what are the other benefits and issues with it?
From javadoc of Gson:
This is the main class for using Gson. Gson is typically used by first constructing a Gson instance and then invoking toJson(Object) or fromJson(String, Class) methods on it. Gson instances are Thread-safe so you can reuse them freely across multiple threads.
So it's ok to have only one Gson instance in your service and reuse it for each request/response. As an example you can consider to create a util class, something like the following class:
public abstract class GsonUtils {
private static Gson gson = new Gson();
static {
// your extra init logic goes here
}
public static <T> T fromJson(String json, Class<T> classOfT) {
return gson.fromJson(json, classOfT);
}
public static String toJson(Object object) {
if (object == null) {
return "";
}
return gson.toJson(object);
}
}
Or you can do it in your way :-)
Recently I was dealing with same idea. There is older thread related to this Is it OK to use Gson instance as a static field in a model bean (reuse)?. Simply summarised the GSON instance should be thread safe.
Related
I save the payload of events as JSON string into a database.
To only have one version of the events in code while still being able to read old events I want to "upcast" the serialized data before deserialization.
I thought about something like this:
public Object deserialize(String data, Class<?> clazz) throws IOException {
data = upcaster.upcast(data, clazz);
return objectMapper.readValue(data, clazz);
}
But this means I transform the string into some JSON object twice. Once for upcasting the data and once inside the standard jackson mapper.
Is there a way to customize jackson between building the json-object and building my event object?
Something like:
#override
updateData(clazz, jsonData) {
if(clazz.equals(SpecificEvent.class)) {
if(!jsonData.containsKey("addedInfo")) {
jsonData.put("addedInfo", "foo");
}
}
}
Alternatively, I could add my own deserializer that changes the given JSON data before calling the standard deserializer I guess?
What's the normal way of doing something like this with jackson?
One option is custom deserialization with a dedicated deserializer class (per your own class). You would then have to maintain the deserializer class to make sure that it provides any missing data.
It should work for a handful of classes, if you have a lot then there might be better ways.
If you try to serialize an object that has a field of type java.lang.Class, serializing it will lead to java.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class: <some_class> Forgot to register a type adapter
Below is the code snippet from com.google.gson.internal.bind.TypeAdapters.java
public final class TypeAdapters {
.
.
.
public static final TypeAdapter<Class> CLASS = new TypeAdapter<Class>() {
#Override
public void write(JsonWriter out, Class value) throws IOException {
if (value == null) {
out.nullValue();
} else {
throw new UnsupportedOperationException("Attempted to serialize java.lang.Class: "
+ value.getName() + ". Forgot to register a type adapter?");
}
}
.
.
.
}
Was this coded in gson just to remind people if they "Forgot to register a type adapter"?
As I see it, Class type object could have easily been serialized and deserialized using the following statements:
Serialize : clazz.getName()
Deserialize : Class.forName(className)
What could the reason behind the current implementation be? Where am I wrong in this?
as answered by #Programmer Bruce Gson not parsing Class variable -
In a comment in issue 340, a Gson project manager explains:
Serializing types is actually somewhat of a security problem, so we
don't want to support it by default. A malicious .json file could
cause your application to load classes that it wouldn't otherwise;
depending on your class path loading certain classes could DoS your
application.
But it's quite straightforward to write a type adapter to support this
in your own app.
Of course, since serialization is not the same as
deserialization, I don't understand how this is an explanation for the
disabled serialization, unless the unmentioned notion is to in a sense
"balance" the default behaviors of serialization with deserialization.
I'm trying to code domain objects that can create themselves from other objects which implement the same interface. I'm also coding it so they can transform themselves into other implementations, basically simple domain transfer objects. I'm using jackson to automatically convert between implementations to reduce error prone boiler plate of manual object conversion.
It's probably easier to show with an example:
//base class
public abstract class DO<T extends Object> {
public abstract T toDTO();
public abstract DO<T> fromDTO(T t);
}
//concrete implementation
public class MyDO extends DO<MyDOInterface> implements MyDOInterface {
public MyDO fromDTO(MyDTO r){
ObjectMapper mapper = new ObjectMapper();
return mapper.convertValue(r, MyDO.class);
}
public MyDTO toDTO() {
ObjectMapper mapper = new ObjectMapper();
return mapper.convertValue(this, MyDO.class);
}
//getters and setters from MyDOInterface
}
Now this works fine when creating DTOs, but is a bit of a pain the other way around. To create my domain objects I'm having to do this:
MyDO myDO = new MyDO().fromDTO(aDTOInstance);
Which creates an empty object in order to call fromDTO(...) on it.
I've got a feeling I'm missing something simple that would either allow me to pass the DTO in a constructor or a static method to avoid this. Or even a factory method in DO itself but I can't work out what it is. Can anyone see a way of making this work?
Maybe have a look at #JsonCreator annotation: it allows you to mark constructors and (static) factory methods to be used. Specifically, so-called "delegating" creator like:
Another possibility when serializing would be #JsonValue, which allows certain conversions during serialization process.
I don't know if these help with specific problem, since you are doing more conversion than reading/writing JSON, but they seem related.
If I just drop in .toJson and .fromJson instead of using .writeObject and .readObject, will I get identical results?
Additionally, what's the GSON equivalent of the readObject and writeObject methods that you can put in classes to be serialized?
Gson doesn't support inheritance or circular references sufficiently to be a drop-in replacement for Java Serialization.
For inheritance, use RuntimeTypeAdapterFactory to encode class names in JSON values.
For circular references, you can use GraphAdapterBuilder to encode values by reference rather than by value.
Neither of these files are included in the Gson distribution. You'll need to copy and paste them into your app if you want to use them.
Gson AFAIK does not have readObject & writeObject (according to it's API)
As for the second part of the question: GSON works like this: you can implement a serializer/deserializer like this:
private class DateTimeSerializer implements JsonSerializer<DateTime> {
public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src.toString());
}
}
and then register them with gson.registerTypeAdapter(DateTime.class, DateTimeSerializer());
Here's the model I implemented:
public class LoginSession {
private static final Gson gson = new Gson();
private String id;
private String name;
private long timestamp;
public LoginSession(String id, String name) {
this.id = id;
this.name = name;
this.timestamp = System.currentTimeMillis();
}
public String toJson() {
return gson.toJson(this);
}
public static LoginSession fromJson(String json) {
checkArgument(!isNullOrEmpty(json));
return gson.fromJson(json, LoginSession.class);
}
}
I thought it's useless to create new Gson instance for every LoginSession instance.
But what I'm worried about is thread-safety issues. Approximately 1000+ instances/sec will be created.
Is it OK to use Gson instance as static field?
Thanks for any advices/corrections.
It seems just fine to me. There is nothing in the GSON instance that makes it related to a specific instance of LoginSession, so it should be static.
GSON instances should be thread-safe, and there was a bug regarding that which was fixed.
The core Gson class is thread-safe. I just encountered a thread-safety issue that was supposedly with GSON. The issue happened when using a custom JsonDeserializer and JsonSerializer for Date parsing and formatting. As it turned out, the thread-safety issue was with my method's use of a static SimpleDateFormat instance which is not thread-safe. Once I wrapped the static SimpleDateFormat in a ThreadLocal instance, everything worked out fine.
According to the comments the existing unit test does not really test much, be careful with anything related to thread safety...
There is a unit test checking for thread safety:
/**
* Tests for ensuring Gson thread-safety.
*
* #author Inderjeet Singh
* #author Joel Leitch
*/
public class ConcurrencyTest extends TestCase {
private Gson gson;
...
You may wonder if this unit test is sufficient to find every possible problem on every possible machine configuration ? Any comments on this ?
There is also this sentence in the docs:
The Gson instance does not maintain any state while invoking Json
operations. So, you are free to reuse the same object for multiple
Json serialization and deserialization operations.
We had issues with thread safety a while back and we solved it by using the FastDateFormat in apache commons.
Just created a gist Link for Gist around this to help the people wondering if Gson instances can be reused. They do not have setters and all vars are private.
So other than the SimpleDateFormat issue I don't see them maintaining state anywhere else.
Do check it out. This is my first time replying to one of these . Happy to give back for once . :)