I'm trying to get the values out of this JSON string but I'm having a hard time achieving this.
{"DebugLogId":"1750550","RequestId":"17505503","Result":
{"Code":"","DebugLogId":"1750550","Message":""},
"Suggestions":[
{"Ranking":"1","Score":"60","Title":"This is a test message 1"},
{"Ranking":"2","Score":"60","Title":"This is a test message 2"}
]}
What way would be easiest to access the data in 'Suggestions'? I'm using the GSON module. Ideally I would like to put it all in a HashMap.
Thanks for any help and/or suggestions!
Thanks for any help!
Hope this helps:
App.java:
package sg.java.play_sof_json_6596072;
import com.google.gson.Gson;
public class App {
public static void main(String[] args) {
Gson gson = new Gson();
String jsonString = "{\"DebugLogId\":\"1750550\",\"RequestId\":\"17505503\",\"Result\":{\"Code\":\"\",\"DebugLogId\":\"1750550\",\"Message\":\"\"},\"Suggestions\":[{\"Ranking\":\"1\",\"Score\":\"60\",\"Title\":\"This is a test message 1\"},{\"Ranking\":\"2\",\"Score\":\"60\",\"Title\":\"This is a test message 2\"}]}";
Debug obj = (Debug) gson.fromJson(jsonString, Debug.class);
System.out.println(obj.getSuggestionList().get(1).getTitle());
}
}
Debug.java:
package sg.java.play_sof_json_6596072;
import java.util.List;
import com.google.gson.annotations.SerializedName;
public class Debug {
#SerializedName("DebugLogId")
private String debugLogId;
#SerializedName("RequestId")
private String requestId;
#SerializedName("Result")
private Result result;
#SerializedName("Suggestions")
private List<Suggestion> suggestionList;
/**
* #return the debugLogId
*/
public final String getDebugLogId() {
return this.debugLogId;
}
/**
* #param debugLogId the debugLogId to set
*/
public final void setDebugLogId(String debugLogId) {
this.debugLogId = debugLogId;
}
/**
* #return the requestId
*/
public final String getRequestId() {
return this.requestId;
}
/**
* #param requestId the requestId to set
*/
public final void setRequestId(String requestId) {
this.requestId = requestId;
}
/**
* #return the result
*/
public final Result getResult() {
return this.result;
}
/**
* #param result the result to set
*/
public final void setResult(Result result) {
this.result = result;
}
/**
* #return the suggestionList
*/
public final List<Suggestion> getSuggestionList() {
return this.suggestionList;
}
/**
* #param suggestionList the suggestionList to set
*/
public final void setSuggestionList(List<Suggestion> suggestionList) {
this.suggestionList = suggestionList;
}
}
Result.java:
package sg.java.play_sof_json_6596072;
import com.google.gson.annotations.SerializedName;
public class Result {
#SerializedName("Code")
private String code;
#SerializedName("DebugLogId")
private String debugLogId;
#SerializedName("Message")
private String messahe;
/**
* #return the code
*/
public final String getCode() {
return this.code;
}
/**
* #param code the code to set
*/
public final void setCode(String code) {
this.code = code;
}
/**
* #return the debugLogId
*/
public final String getDebugLogId() {
return this.debugLogId;
}
/**
* #param debugLogId the debugLogId to set
*/
public final void setDebugLogId(String debugLogId) {
this.debugLogId = debugLogId;
}
/**
* #return the messahe
*/
public final String getMessahe() {
return this.messahe;
}
/**
* #param messahe the messahe to set
*/
public final void setMessahe(String messahe) {
this.messahe = messahe;
}
}
Suggestion.java:
package sg.java.play_sof_json_6596072;
import com.google.gson.annotations.SerializedName;
public class Suggestion {
#SerializedName("Ranking")
private String ranking;
#SerializedName("Score")
private String score;
#SerializedName("Title")
private String title;
/**
* #return the ranking
*/
public final String getRanking() {
return this.ranking;
}
/**
* #param ranking the ranking to set
*/
public final void setRanking(String ranking) {
this.ranking = ranking;
}
/**
* #return the score
*/
public final String getScore() {
return this.score;
}
/**
* #param score the score to set
*/
public final void setScore(String score) {
this.score = score;
}
/**
* #return the title
*/
public final String getTitle() {
return this.title;
}
/**
* #param title the title to set
*/
public final void setTitle(String title) {
this.title = title;
}
}
I'm recommend you to use flexjson library http://flexjson.sourceforge.net/
IMHO, it more simple and usable library. I has used GSON first time, but then switched all my projects to flexjson instead of GSON.
Using the standard json classes in android:
JSONObject o = new JSONObject("your string");
JSONArray a = o.getJSONArray("Suggestions");
int i = 0;
while ( i < a.length())
{
o = a.getJSONObject(i);
//do something with o, like o.getString("Title") ...
++i;
}
Ideally I would like to put it all in a HashMap.
If you can switch libraries, Jackson can achieve that with just one line of code.
Map map = new ObjectMapper().readValue(json, Map.class);
This would deserialize any JSON object into a HashMap, composed of just Java SE components. I haven't yet seen another Java-to/from-JSON library that can do that.
The same can be accomplished with Gson, but it requires a few more lines of code. Here's one such solution.
JsonElement je = new JsonParser().parse(json);
JsonObject jo = je.getAsJsonObject();
Map<String, Object> map = createMapFromJsonObject(jo);
// ...
static Map<String, Object> createMapFromJsonObject(
JsonObject jo)
{
Map<String, Object> map = new HashMap<String, Object>();
for (Entry<String, JsonElement> entry : jo.entrySet())
{
String key = entry.getKey();
JsonElement value = entry.getValue();
map.put(key, getValueFromJsonElement(value));
}
return map;
}
static Object getValueFromJsonElement(JsonElement je)
{
if (je.isJsonObject())
{
return createMapFromJsonObject(je.getAsJsonObject());
}
else if (je.isJsonArray())
{
JsonArray array = je.getAsJsonArray();
List<Object> list = new ArrayList<Object>(array.size());
for (JsonElement element : array)
{
list.add(getValueFromJsonElement(element));
}
return list;
}
else if (je.isJsonNull())
{
return null;
}
else // must be primitive
{
JsonPrimitive p = je.getAsJsonPrimitive();
if (p.isBoolean()) return p.getAsBoolean();
if (p.isString()) return p.getAsString();
// else p is number, but don't know what kind
String s = p.getAsString();
try
{
return new BigInteger(s);
}
catch (NumberFormatException e)
{
// must be a decimal
return new BigDecimal(s);
}
}
}
(I copied this code from my blog post at http://programmerbruce.blogspot.com/2011/06/gson-v-jackson.html.)
Related
I tried below avro schema :
{
"type": "record",
"name": "Tests",
"fields": [
{
"name": "name",
"alias": "first_name",
"type": "string",
"default": ""
}
]
}
when I "compile schema" this using avro tools I get the corresponding java file.
/**
* Autogenerated by Avro
*
* DO NOT EDIT DIRECTLY
*/
import org.apache.avro.generic.GenericArray;
import org.apache.avro.specific.SpecificData;
import org.apache.avro.util.Utf8;
import org.apache.avro.message.BinaryMessageEncoder;
import org.apache.avro.message.BinaryMessageDecoder;
import org.apache.avro.message.SchemaStore;
#org.apache.avro.specific.AvroGenerated
public class Tests extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord {
private static final long serialVersionUID = 8259235364501890516L;
public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"Tests\",\"fields\":[{\"name\":\"name\",\"type\":\"string\",\"default\":\"\",\"alias\":\"first_name\"}]}");
public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; }
private static SpecificData MODEL$ = new SpecificData();
private static final BinaryMessageEncoder<Tests> ENCODER =
new BinaryMessageEncoder<Tests>(MODEL$, SCHEMA$);
private static final BinaryMessageDecoder<Tests> DECODER =
new BinaryMessageDecoder<Tests>(MODEL$, SCHEMA$);
/**
* Return the BinaryMessageEncoder instance used by this class.
* #return the message encoder used by this class
*/
public static BinaryMessageEncoder<Tests> getEncoder() {
return ENCODER;
}
/**
* Return the BinaryMessageDecoder instance used by this class.
* #return the message decoder used by this class
*/
public static BinaryMessageDecoder<Tests> getDecoder() {
return DECODER;
}
/**
* Create a new BinaryMessageDecoder instance for this class that uses the specified {#link SchemaStore}.
* #param resolver a {#link SchemaStore} used to find schemas by fingerprint
* #return a BinaryMessageDecoder instance for this class backed by the given SchemaStore
*/
public static BinaryMessageDecoder<Tests> createDecoder(SchemaStore resolver) {
return new BinaryMessageDecoder<Tests>(MODEL$, SCHEMA$, resolver);
}
/**
* Serializes this Tests to a ByteBuffer.
* #return a buffer holding the serialized data for this instance
* #throws java.io.IOException if this instance could not be serialized
*/
public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException {
return ENCODER.encode(this);
}
/**
* Deserializes a Tests from a ByteBuffer.
* #param b a byte buffer holding serialized data for an instance of this class
* #return a Tests instance decoded from the given buffer
* #throws java.io.IOException if the given bytes could not be deserialized into an instance of this class
*/
public static Tests fromByteBuffer(
java.nio.ByteBuffer b) throws java.io.IOException {
return DECODER.decode(b);
}
private java.lang.CharSequence name;
/**
* Default constructor. Note that this does not initialize fields
* to their default values from the schema. If that is desired then
* one should use <code>newBuilder()</code>.
*/
public Tests() {}
/**
* All-args constructor.
* #param name The new value for name
*/
public Tests(java.lang.CharSequence name) {
this.name = name;
}
public org.apache.avro.specific.SpecificData getSpecificData() { return MODEL$; }
public org.apache.avro.Schema getSchema() { return SCHEMA$; }
// Used by DatumWriter. Applications should not call.
public java.lang.Object get(int field$) {
switch (field$) {
case 0: return name;
default: throw new IndexOutOfBoundsException("Invalid index: " + field$);
}
}
// Used by DatumReader. Applications should not call.
#SuppressWarnings(value="unchecked")
public void put(int field$, java.lang.Object value$) {
switch (field$) {
case 0: name = (java.lang.CharSequence)value$; break;
default: throw new IndexOutOfBoundsException("Invalid index: " + field$);
}
}
/**
* Gets the value of the 'name' field.
* #return The value of the 'name' field.
*/
public java.lang.CharSequence getName() {
return name;
}
/**
* Sets the value of the 'name' field.
* #param value the value to set.
*/
public void setName(java.lang.CharSequence value) {
this.name = value;
}
/**
* Creates a new Tests RecordBuilder.
* #return A new Tests RecordBuilder
*/
public static Tests.Builder newBuilder() {
return new Tests.Builder();
}
/**
* Creates a new Tests RecordBuilder by copying an existing Builder.
* #param other The existing builder to copy.
* #return A new Tests RecordBuilder
*/
public static Tests.Builder newBuilder(Tests.Builder other) {
if (other == null) {
return new Tests.Builder();
} else {
return new Tests.Builder(other);
}
}
/**
* Creates a new Tests RecordBuilder by copying an existing Tests instance.
* #param other The existing instance to copy.
* #return A new Tests RecordBuilder
*/
public static Tests.Builder newBuilder(Tests other) {
if (other == null) {
return new Tests.Builder();
} else {
return new Tests.Builder(other);
}
}
/**
* RecordBuilder for Tests instances.
*/
#org.apache.avro.specific.AvroGenerated
public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase<Tests>
implements org.apache.avro.data.RecordBuilder<Tests> {
private java.lang.CharSequence name;
/** Creates a new Builder */
private Builder() {
super(SCHEMA$);
}
/**
* Creates a Builder by copying an existing Builder.
* #param other The existing Builder to copy.
*/
private Builder(Tests.Builder other) {
super(other);
if (isValidValue(fields()[0], other.name)) {
this.name = data().deepCopy(fields()[0].schema(), other.name);
fieldSetFlags()[0] = other.fieldSetFlags()[0];
}
}
/**
* Creates a Builder by copying an existing Tests instance
* #param other The existing instance to copy.
*/
private Builder(Tests other) {
super(SCHEMA$);
if (isValidValue(fields()[0], other.name)) {
this.name = data().deepCopy(fields()[0].schema(), other.name);
fieldSetFlags()[0] = true;
}
}
/**
* Gets the value of the 'name' field.
* #return The value.
*/
public java.lang.CharSequence getName() {
return name;
}
/**
* Sets the value of the 'name' field.
* #param value The value of 'name'.
* #return This builder.
*/
public Tests.Builder setName(java.lang.CharSequence value) {
validate(fields()[0], value);
this.name = value;
fieldSetFlags()[0] = true;
return this;
}
/**
* Checks whether the 'name' field has been set.
* #return True if the 'name' field has been set, false otherwise.
*/
public boolean hasName() {
return fieldSetFlags()[0];
}
/**
* Clears the value of the 'name' field.
* #return This builder.
*/
public Tests.Builder clearName() {
name = null;
fieldSetFlags()[0] = false;
return this;
}
#Override
#SuppressWarnings("unchecked")
public Tests build() {
try {
Tests record = new Tests();
record.name = fieldSetFlags()[0] ? this.name : (java.lang.CharSequence) defaultValue(fields()[0]);
return record;
} catch (org.apache.avro.AvroMissingFieldException e) {
throw e;
} catch (java.lang.Exception e) {
throw new org.apache.avro.AvroRuntimeException(e);
}
}
}
#SuppressWarnings("unchecked")
private static final org.apache.avro.io.DatumWriter<Tests>
WRITER$ = (org.apache.avro.io.DatumWriter<Tests>)MODEL$.createDatumWriter(SCHEMA$);
#Override public void writeExternal(java.io.ObjectOutput out)
throws java.io.IOException {
WRITER$.write(this, SpecificData.getEncoder(out));
}
#SuppressWarnings("unchecked")
private static final org.apache.avro.io.DatumReader<Tests>
READER$ = (org.apache.avro.io.DatumReader<Tests>)MODEL$.createDatumReader(SCHEMA$);
#Override public void readExternal(java.io.ObjectInput in)
throws java.io.IOException {
READER$.read(this, SpecificData.getDecoder(in));
}
#Override protected boolean hasCustomCoders() { return true; }
#Override public void customEncode(org.apache.avro.io.Encoder out)
throws java.io.IOException
{
out.writeString(this.name);
}
#Override public void customDecode(org.apache.avro.io.ResolvingDecoder in)
throws java.io.IOException
{
org.apache.avro.Schema.Field[] fieldOrder = in.readFieldOrderIfDiff();
if (fieldOrder == null) {
this.name = in.readString(this.name instanceof Utf8 ? (Utf8)this.name : null);
} else {
for (int i = 0; i < 1; i++) {
switch (fieldOrder[i].pos()) {
case 0:
this.name = in.readString(this.name instanceof Utf8 ? (Utf8)this.name : null);
break;
default:
throw new java.io.IOException("Corrupt ResolvingDecoder.");
}
}
}
}
}
I am wondering where is the getter/setter for alias? How can i use it? I understand if I have avro record then I can use the alias but how to I generate Avro record using Java file using that alias?
aliases is responsible for providing an alternate name to a field. Documentation from Apache Avro.
If you update alias to aliases, this will work:
{
"type": "record",
"name": "Tests",
"fields": [
{
"name": "first_name",
"aliases": ["former_name_of_field"],
"type": "string",
"default": ""
}
]
}
This is an optional field to support schema evolution.
/**
* Autogenerated by Avro
*
* DO NOT EDIT DIRECTLY
*/
import org.apache.avro.generic.GenericArray;
import org.apache.avro.specific.SpecificData;
import org.apache.avro.util.Utf8;
import org.apache.avro.message.BinaryMessageEncoder;
import org.apache.avro.message.BinaryMessageDecoder;
import org.apache.avro.message.SchemaStore;
#org.apache.avro.specific.AvroGenerated
public class Tests extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord {
private static final long serialVersionUID = -2440753220882419647L;
public static final org.apache.avro.Schema SCHEMA$ = new org.apache.avro.Schema.Parser().parse("{\"type\":\"record\",\"name\":\"Tests\",\"fields\":[{\"name\":\"first_name\",\"type\":{\"type\":\"string\",\"avro.java.string\":\"String\"},\"default\":\"\",\"aliases\":[\"former_name_of_field\"]}]}");
public static org.apache.avro.Schema getClassSchema() { return SCHEMA$; }
private static SpecificData MODEL$ = new SpecificData();
private static final BinaryMessageEncoder<Tests> ENCODER =
new BinaryMessageEncoder<Tests>(MODEL$, SCHEMA$);
private static final BinaryMessageDecoder<Tests> DECODER =
new BinaryMessageDecoder<Tests>(MODEL$, SCHEMA$);
/**
* Return the BinaryMessageEncoder instance used by this class.
* #return the message encoder used by this class
*/
public static BinaryMessageEncoder<Tests> getEncoder() {
return ENCODER;
}
/**
* Return the BinaryMessageDecoder instance used by this class.
* #return the message decoder used by this class
*/
public static BinaryMessageDecoder<Tests> getDecoder() {
return DECODER;
}
/**
* Create a new BinaryMessageDecoder instance for this class that uses the specified {#link SchemaStore}.
* #param resolver a {#link SchemaStore} used to find schemas by fingerprint
* #return a BinaryMessageDecoder instance for this class backed by the given SchemaStore
*/
public static BinaryMessageDecoder<Tests> createDecoder(SchemaStore resolver) {
return new BinaryMessageDecoder<Tests>(MODEL$, SCHEMA$, resolver);
}
/**
* Serializes this Tests to a ByteBuffer.
* #return a buffer holding the serialized data for this instance
* #throws java.io.IOException if this instance could not be serialized
*/
public java.nio.ByteBuffer toByteBuffer() throws java.io.IOException {
return ENCODER.encode(this);
}
/**
* Deserializes a Tests from a ByteBuffer.
* #param b a byte buffer holding serialized data for an instance of this class
* #return a Tests instance decoded from the given buffer
* #throws java.io.IOException if the given bytes could not be deserialized into an instance of this class
*/
public static Tests fromByteBuffer(
java.nio.ByteBuffer b) throws java.io.IOException {
return DECODER.decode(b);
}
#Deprecated public java.lang.String first_name;
/**
* Default constructor. Note that this does not initialize fields
* to their default values from the schema. If that is desired then
* one should use <code>newBuilder()</code>.
*/
public Tests() {}
/**
* All-args constructor.
* #param first_name The new value for first_name
*/
public Tests(java.lang.String first_name) {
this.first_name = first_name;
}
public org.apache.avro.specific.SpecificData getSpecificData() { return MODEL$; }
public org.apache.avro.Schema getSchema() { return SCHEMA$; }
// Used by DatumWriter. Applications should not call.
public java.lang.Object get(int field$) {
switch (field$) {
case 0: return first_name;
default: throw new IndexOutOfBoundsException("Invalid index: " + field$);
}
}
// Used by DatumReader. Applications should not call.
#SuppressWarnings(value="unchecked")
public void put(int field$, java.lang.Object value$) {
switch (field$) {
case 0: first_name = value$ != null ? value$.toString() : null; break;
default: throw new IndexOutOfBoundsException("Invalid index: " + field$);
}
}
/**
* Gets the value of the 'first_name' field.
* #return The value of the 'first_name' field.
*/
public java.lang.String getFirstName() {
return first_name;
}
/**
* Sets the value of the 'first_name' field.
* #param value the value to set.
*/
public void setFirstName(java.lang.String value) {
this.first_name = value;
}
/**
* Creates a new Tests RecordBuilder.
* #return A new Tests RecordBuilder
*/
public static Tests.Builder newBuilder() {
return new Tests.Builder();
}
/**
* Creates a new Tests RecordBuilder by copying an existing Builder.
* #param other The existing builder to copy.
* #return A new Tests RecordBuilder
*/
public static Tests.Builder newBuilder(Tests.Builder other) {
if (other == null) {
return new Tests.Builder();
} else {
return new Tests.Builder(other);
}
}
/**
* Creates a new Tests RecordBuilder by copying an existing Tests instance.
* #param other The existing instance to copy.
* #return A new Tests RecordBuilder
*/
public static Tests.Builder newBuilder(Tests other) {
if (other == null) {
return new Tests.Builder();
} else {
return new Tests.Builder(other);
}
}
/**
* RecordBuilder for Tests instances.
*/
#org.apache.avro.specific.AvroGenerated
public static class Builder extends org.apache.avro.specific.SpecificRecordBuilderBase<Tests>
implements org.apache.avro.data.RecordBuilder<Tests> {
private java.lang.String first_name;
/** Creates a new Builder */
private Builder() {
super(SCHEMA$);
}
/**
* Creates a Builder by copying an existing Builder.
* #param other The existing Builder to copy.
*/
private Builder(Tests.Builder other) {
super(other);
if (isValidValue(fields()[0], other.first_name)) {
this.first_name = data().deepCopy(fields()[0].schema(), other.first_name);
fieldSetFlags()[0] = other.fieldSetFlags()[0];
}
}
/**
* Creates a Builder by copying an existing Tests instance
* #param other The existing instance to copy.
*/
private Builder(Tests other) {
super(SCHEMA$);
if (isValidValue(fields()[0], other.first_name)) {
this.first_name = data().deepCopy(fields()[0].schema(), other.first_name);
fieldSetFlags()[0] = true;
}
}
/**
* Gets the value of the 'first_name' field.
* #return The value.
*/
public java.lang.String getFirstName() {
return first_name;
}
/**
* Sets the value of the 'first_name' field.
* #param value The value of 'first_name'.
* #return This builder.
*/
public Tests.Builder setFirstName(java.lang.String value) {
validate(fields()[0], value);
this.first_name = value;
fieldSetFlags()[0] = true;
return this;
}
/**
* Checks whether the 'first_name' field has been set.
* #return True if the 'first_name' field has been set, false otherwise.
*/
public boolean hasFirstName() {
return fieldSetFlags()[0];
}
/**
* Clears the value of the 'first_name' field.
* #return This builder.
*/
public Tests.Builder clearFirstName() {
first_name = null;
fieldSetFlags()[0] = false;
return this;
}
#Override
#SuppressWarnings("unchecked")
public Tests build() {
try {
Tests record = new Tests();
record.first_name = fieldSetFlags()[0] ? this.first_name : (java.lang.String) defaultValue(fields()[0]);
return record;
} catch (org.apache.avro.AvroMissingFieldException e) {
throw e;
} catch (java.lang.Exception e) {
throw new org.apache.avro.AvroRuntimeException(e);
}
}
}
#SuppressWarnings("unchecked")
private static final org.apache.avro.io.DatumWriter<Tests>
WRITER$ = (org.apache.avro.io.DatumWriter<Tests>)MODEL$.createDatumWriter(SCHEMA$);
#Override public void writeExternal(java.io.ObjectOutput out)
throws java.io.IOException {
WRITER$.write(this, SpecificData.getEncoder(out));
}
#SuppressWarnings("unchecked")
private static final org.apache.avro.io.DatumReader<Tests>
READER$ = (org.apache.avro.io.DatumReader<Tests>)MODEL$.createDatumReader(SCHEMA$);
#Override public void readExternal(java.io.ObjectInput in)
throws java.io.IOException {
READER$.read(this, SpecificData.getDecoder(in));
}
#Override protected boolean hasCustomCoders() { return true; }
#Override public void customEncode(org.apache.avro.io.Encoder out)
throws java.io.IOException
{
out.writeString(this.first_name);
}
#Override public void customDecode(org.apache.avro.io.ResolvingDecoder in)
throws java.io.IOException
{
org.apache.avro.Schema.Field[] fieldOrder = in.readFieldOrderIfDiff();
if (fieldOrder == null) {
this.first_name = in.readString();
} else {
for (int i = 0; i < 1; i++) {
switch (fieldOrder[i].pos()) {
case 0:
this.first_name = in.readString();
break;
default:
throw new java.io.IOException("Corrupt ResolvingDecoder.");
}
}
}
}
}
I do not believe it is possible to set aliases via Java code.
I want my program print all the entries in HashMap, and it does if I run the app in debugger. But if I run it normaly it prints only the last entry :(
Have no idea why it does,
Please help me.
MoneyCounter:
package home.lib;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import home.interfaces.GoodsOper;
import home.interfaces.WalletOper;
import home.interfaces.WastesListOper;
public class MoneyCounter implements WastesListOper, WalletOper, GoodsOper {
private ArrayList<String> wastesPrint = new ArrayList<>();
private Map<GregorianCalendar, Good> wastesMap = new HashMap<GregorianCalendar, Good>();
private Map<String, Good> goodsMap = new HashMap<String, Good>();
private Map<String, Wallet> walletsMap = new HashMap<String, Wallet>();
private Wallet currentWallet;
private Good currentGood;
private SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-YYYY");
/**
* Provides selling (returning) of specified good puts the good
* to the wastesMap by date
* #param goodName
*/
#Override
public void sell(String goodName) {
// TODO implement the summation of wastes
if(goodsMap.containsKey(goodName)){
putMoney(goodsMap.get(goodName).getPrice());
wastesMap.put(new GregorianCalendar(), goodsMap.get(goodName));
}
}
/**
* Provides buying specified good puts the good you've bought
* to the wastesMap by date
* #param goodName
*/
#Override
public void buy(String goodName) {
// TODO implement the summation of wastes
if(goodsMap.containsKey(goodName)){
takeMoney(goodsMap.get(goodName).getPrice());
wastesMap.put(new GregorianCalendar(), goodsMap.get(goodName));
}
}
/**
* Add a new Wallet to the list if there is no the same one
* #param name
* #param currency
*/
#Override
public void createWallet(String name, String currency) {
walletsMap.putIfAbsent(name, new Wallet(name, currency));
}
/**
* Adds a new Good to the list with specified price
* #param name
* #param price
*/
#Override
public void createGood(String name, int price) {
goodsMap.putIfAbsent(name, new Good(name, price));
}
/**
* Returns array of strings with goods specifications, which
* satisfies the interval [startPrice, endPrice]
* #param startPrice
* #param endPrice
* #return array of strings String[]
*/
#Override
public String[] goodsListToStringByPrice(int startPrice, int endPrice) {
String[] goods = new String[goodsMap.size()];
int i = 0;
for (Map.Entry<String, Good> e : goodsMap.entrySet()) {
if (e.getValue().getPrice() >= startPrice && e.getValue().getPrice() <= endPrice) {
goods[i++] = e.getValue().goodToString();
}
}
return goods;
}
/**
* Returns an array of Strings with goods descriptions
* #return array of strings String[]
*/
#Override
public String[] goodsListToString() {
String[] goods = new String[goodsMap.size()];
int i = 0;
for (Map.Entry<String, Good> e : goodsMap.entrySet()) {
goods[i++] = e.getValue().goodToString();
}
return goods;
}
/**
* Replaces old Wallet's name with new one specified if one's name is absent
* #param oldName
* #param newName
*/
public void changeWalletName(String oldName, String newName) {
walletsMap.putIfAbsent(newName, new Wallet(newName, walletsMap.get(oldName)));
walletsMap.remove(oldName);
}
/**
* Returns an array of Strings with wallet descriptions
* #return array of strings String[]
*/
public String[] walletListToString() {
String[] wallets = new String[walletsMap.size()];
int i = 0;
for (Map.Entry<String, Wallet> e : walletsMap.entrySet()) {
wallets[i++] = e.getValue().walletToString();
}
return wallets;
}
/**
* Returns the wallet's money balance by name
* #param walletName
* #return String
*/
public String checkWallet(String walletName) {
return walletsMap.get(walletName).walletToString();
}
/**
* Deletes the wallet, specified by name from wallet list
* #param walletName
*/
#Override
public void delWallet(String walletName) {
walletsMap.remove(walletName);
}
/**
* Deletes the good, specified by name from good list
* #param goodName
*/
#Override
public void delGood(String goodName) {
goodsMap.remove(goodName);
}
/**
* Use this method to put more money to the wallet
* got payment for example
* #param count
*/
#Override
public void putMoney(int count) {
currentWallet.addMoney(count);
}
/**
* Use this method if you need money but not for buying a good
* #param count
*/
#Override
public void takeMoney(int count) {
currentWallet.getMoney(count);
}
/**
* Returns list of all wallets
* #return ArrayList
*/
#Override
public ArrayList<String> walletsListToString() {
ArrayList<String> array = new ArrayList<>();
for (Map.Entry<String, Wallet> entry : walletsMap.entrySet()) {
array.add(entry.getValue().walletToString());
}
return array;
}
/**
* Returns list of wallets specified by currency
* #param currency
* #return ArrayList
*/
#Override
public ArrayList<String> walletsListToStringByCurrency(String currency) {
ArrayList<String> array = new ArrayList<>();
for (Map.Entry<String, Wallet> entry : walletsMap.entrySet()) {
if (entry.getValue().getCurrency().equals(currency)) {
array.add(entry.getValue().walletToString());
}
}
return array;
}
/**
* Chooses wallet to operate with when you bus, sell, etc.
* #param walletName
*/
#Override
public void chooseTheWallet(String walletName) {
if (walletsMap.containsKey(walletName)) {
this.currentWallet = walletsMap.get(walletName);
}
}
/**
* Returns list of strings of all money wastes you've ever done
* #return ArrayList wastesPrint
*/
#Override
public void wastesListFillUp() {
for(Map.Entry<GregorianCalendar, Good> entry:wastesMap.entrySet()){
this.wastesPrint.add(dateFormat.format(entry.getKey().getTime())+" "+entry.getValue().goodToString()+
" "+currentWallet.walletToString());
}
}
/**
* Is used for tests
* #throws IOException
*/
public void printAllList() throws IOException {
for (Map.Entry<GregorianCalendar, Good> entry : wastesMap.entrySet()) {
System.out.println(dateFormat.format(entry.getKey().getTime())+" "+entry.getValue().goodToString()+
" "+currentWallet.walletToString());
}
}
/**
* Changes the specified good's price
* #param price
*/
#Override
public void changePrice(int price) {
currentGood.changePrice(price);
}
/**
* Chooses the good for operations
* #param goodName
*/
#Override
public void chooseTheGood(String goodName) {
if (goodsMap.containsKey(goodName)) {
this.currentGood = goodsMap.get(goodName);
}
}
}
Main:
package home.main;
import java.io.IOException;
import home.lib.MoneyCounter;
public class Main {
public static void main(String[] args) throws IOException {
MoneyCounter application = new MoneyCounter();
application.createGood("Snikers", 850);
application.createGood("Хрень какая-то", 1000);
application.createWallet("Основоной счет", "UAH");
application.chooseTheWallet("Основоной счет");
application.buy("Snikers");
application.buy("Хрень какая-то");
application.printAllList();
}
}
Wallet:
package home.lib;
public class Wallet {
// all money is kept
private int moneyCount;
private int debt;
private String currency;
private String name;
// constructor for new Wallet
public Wallet(String walletName, String currencyName) {
this.currency = currencyName;
this.moneyCount = 0;
this.debt = 0;
this.name = walletName;
}
// for renaming Wallet in WalletList
public Wallet(String walletName, Wallet oldWallet) {
this.name = walletName;
this.moneyCount = oldWallet.getMoneyCount();
this.debt = oldWallet.getDebt();
this.currency = oldWallet.getCurrency();
}
// actions with money
public void addMoney(int moneyCount) {
if (this.moneyCount >= 0 && this.debt == 0) {
this.moneyCount += moneyCount;
} else {
moneyCount -= this.debt;
this.debt = 0;
this.moneyCount = moneyCount;
}
}
public void getMoney(int moneyCount) {
if (this.debt == 0 && this.moneyCount > 0 && this.moneyCount >= moneyCount) {
this.moneyCount -= moneyCount;
} else {
moneyCount -= this.moneyCount;
this.moneyCount = 0;
this.debt += moneyCount;
}
}
// getters/setters block
public int getMoneyCount() {
return this.moneyCount;
}
public int getDebt() {
return this.debt;
}
public String getName() {
return this.name;
}
public String getCurrency() {
return this.currency;
}
public String walletToString() {
return this.debt <= 0 ? "("+this.name + " Остаток: " + (double)this.moneyCount/100 + " " + this.currency+")"
: "("+this.name + " Долг: -" + (double)this.debt/100 + " " + this.currency+")";
}
}
Good:
package home.lib;
public class Good {
private int price;
private String name;
public Good(String goodName, int goodPrice) {
this.name = goodName;
this.price = goodPrice;
}
public int getPrice() {
return this.price;
}
public double getPriceInDouble() {
return (double)this.price / 100;
}
public void changePrice(int price) {
this.price = price;
}
public String getName() {
return this.name;
}
public String goodToString() {
return "Товар: " + this.name + " | Цена: " + (double) this.price / 100;
}
}
I've got an answer. I used GregorainCalendar as a HashMap key. So, when I was starting program normaly, it was done in no time. So it was adding different values for ONE key. When you run the program in debugger you can't press next step 10 times in one moment so the time is different, so the keys are different too, so it returns all entries.
Be attentive when use time and all will be Ok :)
I am getting this warning when using Gson to convert a JSON string to a Java object. Why am I getting it and how can I solve it?
This is the warning I getting in code:
Unchecked assignment:
'net.brawtasports.brawtasportsgps.model.JSONKeys' to
'net.brawtasports.brawtasportsgps.model.JSONKeys<net.brawtasports.brawtasportsgps.model.team.Object>'
This is the error I get at runtime:
java.lang.ClassCastException:
com.google.gson.internal.LinkedTreeMap cannot be cast to
net.brawtasports.brawtasportsgps.model.team.Object
And this is my code:
String jsonString = Preferences.readFromPreferences(ApplicationConstants.team_data,"");
Gson gson = new Gson();
JSONKeys<Object> teamMembers = gson.fromJson(jsonString, JSONKeys.class); //converts json string to java object
Object players = teamMembers.getObject();//object is my custom class
//ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(getActivity(),android.R.layout.simple_spinner_dropdown_item,players);
ArrayAdapter arrayAdapter = new ArrayAdapter(getActivity(),android.R.layout.simple_spinner_dropdown_item,players.getPlayersSummary());
player1.setAdapter(arrayAdapter);
Here is the code for my JSONKeys POJO:
public class JSONKeys<T> {
private boolean Success;
private String Message;
private int ObjectIdentifier;
private T Object;
private java.util.List<List> List = new ArrayList<List>();
private int TotalRecords;
private String ErrorMessage;
private int Code;
private net.brawtasports.brawtasportsgps.model.match.Criteria Criteria;
private net.brawtasports.brawtasportsgps.model.match.SearchInfo SearchInfo;
//Add Object from match class
/**
* #return The Criteria
*/
public net.brawtasports.brawtasportsgps.model.match.Criteria getCriteria() {
return Criteria;
}
/**
* #param Criteria The Criteria
*/
public void setCriteria(net.brawtasports.brawtasportsgps.model.match.Criteria Criteria) {
this.Criteria = Criteria;
}
/**
* #return The SearchInfo
*/
public net.brawtasports.brawtasportsgps.model.match.SearchInfo getSearchInfo() {
return SearchInfo;
}
/**
* #param SearchInfo The SearchInfo
*/
public void setSearchInfo(net.brawtasports.brawtasportsgps.model.match.SearchInfo SearchInfo) {
this.SearchInfo = SearchInfo;
}
/**
* #return The List
*/
public java.util.List<List> getList() {
return List;
}
/**
* #param List The List
*/
public void setLArrayListList(java.util.List<List> List) {
this.List = List;
}
/**
* #return The Code
*/
public int getCode() {
return Code;
}
/**
* #param Code The Code
*/
public void setCode(int Code) {
this.Code = Code;
}
/**
* #return The TotalRecords
*/
public int getTotalRecords() {
return TotalRecords;
}
/**
* #param TotalRecords The TotalRecords
*/
public void setTotalRecords(int TotalRecords) {
this.TotalRecords = TotalRecords;
}
/**
* #return The ErrorMessage
*/
public String getErrorMessage() {
return ErrorMessage;
}
/**
* #param ErrorMessage The ErrorMessage
*/
public void setErrorMessage(String ErrorMessage) {
this.ErrorMessage = ErrorMessage;
}
/**
*
* #return
* The message
*/
public String getMessage() {
return Message;
}
/**
*
* #param message
* The message
*/
public void setMessage(String message) {
this.Message = message;
}
/**
*
* #return
* The Success
*/
public boolean isSuccess() {
return Success;
}
/**
*
* #param Success
* The Success
*/
public void setSuccess(boolean Success) {
this.Success = Success;
}
/**
*
* #return
* The ObjectIdentifier
*/
public int getObjectIdentifier() {
return ObjectIdentifier;
}
/**
*
* #param ObjectIdentifier
* The ObjectIdentifier
*/
public void setObjectIdentifier(int ObjectIdentifier) {
this.ObjectIdentifier = ObjectIdentifier;
}
/**
*
* #return
* The Object
*/
public T getObject() {
return Object;
}
/**
*
* #param Object
* The Object
*/
public void setObject(T Object) {
this.Object = Object;
}
}
and for my Object POJO it is:
public class Object {
private HomeTeamGoals HomeTeamGoals;
private AwayTeamGoals AwayTeamGoals;
private List<PlayersSummary> PlayersSummary = new ArrayList<PlayersSummary>();
private TeamSummary TeamSummary;
/**
*
* #return
* The HomeTeamGoals
*/
public HomeTeamGoals getHomeTeamGoals() {
return HomeTeamGoals;
}
/**
*
* #param HomeTeamGoals
* The HomeTeamGoals
*/
public void setHomeTeamGoals(HomeTeamGoals HomeTeamGoals) {
this.HomeTeamGoals = HomeTeamGoals;
}
/**
*
* #return
* The AwayTeamGoals
*/
public AwayTeamGoals getAwayTeamGoals() {
return AwayTeamGoals;
}
/**
*
* #param AwayTeamGoals
* The AwayTeamGoals
*/
public void setAwayTeamGoals(AwayTeamGoals AwayTeamGoals) {
this.AwayTeamGoals = AwayTeamGoals;
}
/**
*
* #return
* The PlayersSummary
*/
public List<PlayersSummary> getPlayersSummary() {
return PlayersSummary;
}
/**
*
* #param PlayersSummary
* The PlayersSummary
*/
public void setPlayersSummary(List<PlayersSummary> PlayersSummary) {
this.PlayersSummary = PlayersSummary;
}
/**
*
* #return
* The TeamSummary
*/
public TeamSummary getTeamSummary() {
return TeamSummary;
}
/**
*
* #param TeamSummary
* The TeamSummary
*/
public void setTeamSummary(TeamSummary TeamSummary) {
this.TeamSummary = TeamSummary;
}
}
You need to use a fully specified type when deserializing generics. They key line that needs changing is here:
JSONKeys<Object> teamMembers = gson.fromJson(jsonString, JSONKeys.class);
Because you're using JSONKeys.class, Gson doesn't know what the generic type of JSONKeys is. Instead, use this:
JSONKeys<Object> teamMembers = gson.fromJson(jsonString,
new TypeToken<JSONKeys<net.brawtasports.brawtasportsgps.model.team.Object>>(){}.getType());
This will tell Gson which generic type you need to use. See my explanation for why this works here: How does Gson TypeToken work?
As an aside, don't name your classes the same thing as anything in the java.lang package. It makes your code really confusing for people reading it and can cause unforseen bugs when you accidentally refer to the wrong one by forgetting an import statement.
We are co-developing a GWT web app that already has working RPC calls for other modules. We built a new RPC module (based on the existing architecture) that compiles and runs, but fails throwing a run-time exception on the following line:
this.dashboardService = GWT.create(DashboardService.class);
The last line in the console is "Uncaught exception escaped" followed by the stack trace to the GWT.create() line above, which is preceded by the console error message:
Deferred binding failed for ... expect subsequent failures [ERROR]
Before those two lines is a laundry list of red errors that begins as follows:
[INFO] [(...)] - Module ... has been loaded
[DEBUG] [(...)] - Rebinding (...).DashboardService
[DEBUG] [(...)] - Invoking generator com.google.gwt.user.rebind.rpc.ServiceInterfaceProxyGenerator
[DEBUG] [(...)] - Generating client proxy for remote service interface '(...).DashboardService'
[INFO] [(...)] - Checking type argument 0 of type 'java.util.Arrays.ArrayList' because it is exposed as an array with a maximum dimension of 1 in this type or one of its subtypes (reached via (...).DashboardChannelSummary)
.
.
.
(more errors without stack trace or line numbers)
The console asks "Did you forget to inherit a module?" but based upon my research, this is not the problem; the problem is somewhere in the GWT deferred binding process, which is not visible in the stack trace. I suspect the bolded line above to be the issue but I cannot make heads or tales of this error message without a line number. Here is the code with proprietary package /module names replaced with (...):
web.xml
<servlet>
<servlet-name>(...) DashboardService
</servlet-name>
<servlet-class>(...).server.DashboardServiceImpl
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>(...) DashboardService
</servlet-name>
<url-pattern>/(...)/DashboardService</url-pattern>
</servlet-mapping>
DashboardChannelSummary.java
/**
* This class is an in memory representation of the results of a document search
*/
public class DashboardChannelSummary implements IsSerializable {
/** Only searches for documents from the past in this amount (the past week) */
private static final int DEFAULT_DASHBOARD_HISTORY_IN_DAYS = -7;
/** array of channels */
private List<Channel> channels;
/** iterator */
private Iterator<Channel> iterator;
/** */
private final static String IMAGE_PATH = "/images/channels/";
/** */
private final static String IMAGE_EXT = ".png";
/** constant for the channel header name */
public final static String BUSINESS_LABEL = "business aviation";
/** constant for the channel header name */
public final static String COMMERCIAL_LABEL = "commercial aviation";
/** constant for the channel header name */
public final static String MRO_LABEL = "mro";
/** constant for the channel header name */
public final static String DEFENSE_LABEL = "defense";
/**
*
*/
public enum CHANNEL_NAME {
BUSINESS (BUSINESS_LABEL, DocumentSummary.BA_ID),
COMMERCIAL (COMMERCIAL_LABEL, DocumentSummary.CA_ID),
MRO (MRO_LABEL, DocumentSummary.MRO_ID),
DEFENSE (DEFENSE_LABEL, DocumentSummary.DEFENSE_ID);
/** */
public String label;
/** */
public int ID;
/** */
private CHANNEL_NAME(String label, int ID) {
this.label = label.toUpperCase();
this.ID = ID;
}
};
/**
*
*/
public static List<String> channelNames() {
ArrayList<String> channels = new ArrayList<String>(CHANNEL_NAME.values().length);
for(int i=0; i<channels.size(); i++) {
channels.add(CHANNEL_NAME.values()[i].label);
}
return channels;
}
/**
*
*/
public static int[] channelIDs() {
int[] IDs = new int[CHANNEL_NAME.values().length];
for(int i=0; i<IDs.length; i++) {
IDs[i] = CHANNEL_NAME.values()[i].ID;
}
return IDs;
}
/**
*
* #return
*/
public static int channelCount() {
return CHANNEL_NAME.values().length;
}
/**
*
*/
public static Date cutoffDate() {
Date date = new Date(0);
CalendarUtil.addDaysToDate(date, DEFAULT_DASHBOARD_HISTORY_IN_DAYS);
return date;
}
/**
*
*/
public class Channel {
/** the name of this channel */
private CHANNEL_NAME name;
/** The list of documents */
private List<DocumentSummary> docs;
/** the iterator */
private Iterator<DocumentSummary> iterator;
/**
*
*/
public Channel(List<DocumentSummary> docs, CHANNEL_NAME name) {
this.docs = docs;
this.name = name;
iterator = docs.iterator();
}
/**
*
*/
public String getLabel() {
return name.label;
}
/**
*
*/
public List<DocumentSummary> getDocuments() {
return docs;
}
/**
*
*/
public boolean hasDocuments() {
return iterator.hasNext();
}
/**
*
* #return
*/
public DocumentSummary nextDocument() {
if(iterator.hasNext()) {
return iterator.next();
}
else {
return null;
}
}
/**
*
*/
public String nextImageURL() {
return GWT.getHostPageBaseURL().concat(IMAGE_PATH + String.valueOf(Random.nextInt(channels.size()) - 1) + IMAGE_EXT);
}
}
/**
* Constructor
*/
public DashboardChannelSummary() {
channels = new ArrayList<Channel>(CHANNEL_NAME.values().length);
iterator = channels.iterator();
}
/**
* Constructor
*/
public DashboardChannelSummary(List<List<DocumentSummary>> channelList) {
channels = new ArrayList<Channel>(CHANNEL_NAME.values().length);
iterator = channels.iterator();
int count = 0;
for(List<DocumentSummary> channelData : channelList)
{
channels.add(new Channel(channelData, CHANNEL_NAME.values()[count++]));
}
}
/**
* #return
*/
public List<Channel> getChannels() {
return channels;
}
/**
* #return
*/
public Channel getChannel(int channel) {
return channels.get(channel);
}
/**
* #return
*/
public Channel nextChannel() {
if(iterator.hasNext()) {
return iterator.next();
}
else {
return null;
}
}
/**
* #return
*/
public List<DocumentSummary> getDocuments(int channel) {
return this.getChannel(channel).getDocuments();
}
}
DashboardPresenter.java:
private final DashboardServiceAsync dashboardService;
and the deferred binding that fails in the Constructor:
this.dashboardService = GWT.create(DashboardService.class);
DashboardServiceAsync.java:
public interface DashboardServiceAsync {
/**
*
* #param channelIDs
* #param startDate
* #param async
*/
void getChannelSummary(int[] channelIDs, Date startDate, AsyncCallback<DashboardChannelSummary> async);
}
DashboardService.java:
#RemoteServiceRelativePath("DashboardService") public interface DashboardService extends RemoteService {
/**
*
* #param channelIDs
* #param startDate
* #return
*/
DashboardChannelSummary getChannelSummary(int[] channelIDs, Date startDate);
}
On the server:
DashboardServiceImpl.java:
public class DashboardServiceImpl extends RemoteServiceServlet implements DashboardService {
/**
*
* #param channelIDs
* #param startDate
* #return
*/
#Override
public DashboardChannelSummary getChannelSummary(int[] channelIDs, Date startDate) {
return new DashboardDaoImpl().getChannelSummary(channelIDs, startDate);
}
}
We have double and triple checked our RPC code for accuracy based on the documentation and the suggestions on SO, such as making sure the method signatures are correct across all interfaces and implementations. Anything obviously wrong jumping out to anyone? Is there a way we can we debug this error with more detail?
UPDATE
DashboardChannelSummary.java redesigned for max efficiency in transporting the data from the server to the client, with all properties now "serializable:"
/**
* This class is an in memory representation of the results of a document search.
*/
public class DashboardChannelSummary implements IsSerializable {
/** Only searches for documents from the past in this amount (the past week) */
private static final int DEFAULT_DASHBOARD_HISTORY_IN_DAYS = -7;
/** array of channels */
private ArrayList<ArrayList<DocumentSummary>> channels;
/** */
private int channel = 0;
/** */
private int image = 0;
/** */
private int index = 0;
/** */
private int last = 0;
/** */
private final static String IMAGE_PATH = "images/channels/";
/** */
private final static String IMAGE_EXT = ".jpg";
/** constant for the channel header name */
public final static String BUSINESS_LABEL = "business";
/** constant for the channel header name */
public final static String COMMERCIAL_LABEL = "commercial";
/** constant for the channel header name */
public final static String MRO_LABEL = "mro";
/** constant for the channel header name */
public final static String DEFENSE_LABEL = "defense";
/**
*
*/
public enum CHANNEL_NAME {
BUSINESS (BUSINESS_LABEL, DocumentSummary.BA_ID, "bus"),
COMMERCIAL (COMMERCIAL_LABEL, DocumentSummary.CA_ID, "_com"),
MRO (MRO_LABEL, DocumentSummary.MRO_ID, "mro"),
DEFENSE (DEFENSE_LABEL, DocumentSummary.DEFENSE_ID, "mil");
/** */
public String label;
/** */
public int ID;
/** */
public String prefix;
/** */
private CHANNEL_NAME(String label, int ID, String prefix) {
this.label = label.toUpperCase();
this.ID = ID;
this.prefix = prefix;
}
};
/**
*
*/
private String nextRandomImage() {
while(index == last) {
index = Random.nextInt(channels.size()) + 1;
}
last = index;
return String.valueOf(index);
}
/**
*
*/
public static List<String> channelNames() {
ArrayList<String> channels = new ArrayList<String>(CHANNEL_NAME.values().length);
for(int i=0; i<channels.size(); i++) {
channels.add(CHANNEL_NAME.values()[i].label);
}
return channels;
}
/**
*
*/
public static int[] channelIDs() {
int[] IDs = new int[CHANNEL_NAME.values().length];
for(int i=0; i<IDs.length; i++) {
IDs[i] = CHANNEL_NAME.values()[i].ID;
}
return IDs;
}
/**
*
* #return
*/
public static int channelCount() {
return CHANNEL_NAME.values().length;
}
/**
*
*/
public static Date cutoffDate() {
Date date = new Date();
CalendarUtil.addDaysToDate(date, DEFAULT_DASHBOARD_HISTORY_IN_DAYS);
return date;
}
/**
* Constructor
*/
public DashboardChannelSummary() {
}
/**
* Constructor
*/
public DashboardChannelSummary(ArrayList<ArrayList<DocumentSummary>> channels) {
this.channels = channels;
}
/**
*
*/
public String nextImageURL() {
if(++image > channels.get(channel - 1).size()) {
image = 0;
}
return GWT.getHostPageBaseURL() +
IMAGE_PATH +
CHANNEL_NAME.values()[channel - 1].prefix +
nextRandomImage() +
IMAGE_EXT;
}
/**
*
*/
public ArrayList<DocumentSummary> nextChannel() {
return channels.get(channel++);
}
/**
*
*/
public String toString() {
return this.getClass().toString() + " current channel : " + channel;
}
}
The culprit is most likely DashboardChannelSummary. To be sure, change the return type of getChannelSummary to something "safe", like String or just Void. If the error persists, there's an issue with the service's configuration (though, I doubt it would come up during GWT's compilation phase). If this service works, then you can be pretty sure that it's because DashboardChannelSummary is not serializable.
While the class itself has a no-args constructor and implements IsSerializable, not all the fields are serializable. You should have a closer look at the Channel class (maybe DocumentSummary too, but there's no code for it provided in the question) and the Iterator fields (ArrayList returns an Itr instance, which doesn't seem to be serializable).
If the error still persists, try simplifying DashboardChannelSummary until you get a working version and then work your way "up", until you find the part that is causing the error.
We have a memory leak problem, we don't know what/where too many instances of a certain class are created/referred from. This occurs under heavy load in production and we cannot obtain heap dump (taking heap dump hangs the HA server for too long time). Runtime profiling is also not an option on production site because of performance degradation, the customers are happier with a random crash rather than agonizing slow during monitoring trying to fish for the crash instant. We don't know how to initiate the crash (leak), it just occurs at some times.
Is there a way to obtain object referrers/instantiation points at runtime from within the application itself?
I looked at http://docs.oracle.com/javase/6/docs/jdk/api/jpda/jdi/com/sun/jdi/ObjectReference.html and it gives an idea that something like this could be possible.
Any pointers how to achieve this preferrably with custom code without the heap-dump-way? Reproducing the problem in test environment has been tried and it seems exhaustive wild goose-chase. We want now a brute force way to find the cause.
It is recommended that you try to check you code which is causing such leaks. Here are some tutorials and help regarding the same
IBM Article on Handling memory leaks in Java
http://www.ibm.com/developerworks/library/j-leaks/
Some other useful articles
http://www.openlogic.com/wazi/bid/188158/How-to-Fix-Memory-Leaks-in-Java
There is also an Eclipse Memory Analyser Tool
But the most recommended solution will be
Try running jvisualvm from the JVM on the same machine as your program is running and enable profiling.
We solved the issue by collecting stacktraces on instantiation and on clone.. and by dumping them on a scheduler and when memory goes low.
We know the Object class that causes the problem, just needed to hunt down where it is born:
#EntityListeners(AbstractDTOJpaEventListener.class)
#MappedSuperclass
public abstract class AbstractDTO implements Storeable, Serializable, Cloneable {
/** */
private static final String SHADOWED_CLASS = "Custom";
/** */
protected final static boolean DEBUG_CUSTOM_INSTANCES = true;
/** */
public static long TARGET_HITRATE_PER_INTERVAL = 400000;
/** */
public static long LOGGING_INTERVAL = Times.MILLISECONDS_IN_TEN_SECONDS;
/** */
private static long previousLoggingTime;
/** */
protected static int hits;
/** */
protected static boolean hitting;
/** */
protected static int hitsWithinInterval;
/**
* #author Martin
*/
public static class Hi {
/**
*
*/
private long hitted;
private final long createdAt;
private final StackTraceElement[] stackTraceElements;
private final String threadName;
/**
* #param threadName
* #param stackTraceElements
*/
public Hi(String threadName, StackTraceElement[] stackTraceElements) {
this.threadName = threadName;
this.createdAt = System.currentTimeMillis();
this.stackTraceElements = stackTraceElements;
}
/**
*
*/
public void hit() {
hitted++;
}
/**
* #return the hitted
*/
public long getHitted() {
return hitted;
}
/**
* #param hitted the hitted to set
*/
public void setHitted(long hitted) {
this.hitted = hitted;
}
/**
* #return the createdAt
*/
public long getCreatedAt() {
return createdAt;
}
/**
* #return the stackTraceElements
*/
public StackTraceElement[] getStackTraceElements() {
return stackTraceElements;
}
/**
* #return the threadName
*/
public String getThreadName() {
return threadName;
}
}
/** */
protected final static Map<String, Hi> INSTANCE_SHADOW = new ConcurrentHashMap<String, Hi>();
private static final Comparator<? super Entry<String, Hi>> COMPARATOR = new Comparator<Entry<String, Hi>>() {
#Override
public int compare(Entry<String, Hi> o1, Entry<String, Hi> o2) {
if (o1 == o2) {
return 0;
}
return -Utils.compareNullSafe(o1.getValue().getHitted(), o2.getValue().getHitted(), Compare.ARG0_FIRST);
}
};
/**
* #param <T>
* #return T
* #see java.lang.Object#clone()
*/
#SuppressWarnings("unchecked")
public <T extends AbstractDTO> T clone() {
try {
return (T) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
} finally {
if (DEBUG_CUSTOM_INSTANCES && getClass().getSimpleName().equals(SHADOWED_CLASS)) {
shadowInstance();
}
}
}
/**
*
*/
protected void shadowInstance() {
if (DEBUG_CUSTOM_INSTANCES) {
final long currentTimeMillis = System.currentTimeMillis();
if (TARGET_HITRATE_PER_INTERVAL <= ++hitsWithinInterval) {
hitting = true;
}
if ((TARGET_HITRATE_PER_INTERVAL / 2) <= ++hits) {
final Thread currentThread = Thread.currentThread();
final StackTraceElement[] stackTrace = currentThread.getStackTrace();
final String key = Utils.getPropertyPath(String.valueOf(System.identityHashCode(currentThread)), displayStackLocaktion(stackTrace))
.intern();
Hi hi = INSTANCE_SHADOW.get(key);
if (hi == null) {
synchronized (key) {
hi = INSTANCE_SHADOW.get(key);
if (hi == null) {
INSTANCE_SHADOW.put(key, hi = new Hi(currentThread.getName(), stackTrace));
}
}
}
hi.hit();
}
{
if (getLoggingInterval(currentTimeMillis) != getLoggingInterval(previousLoggingTime)) {
if (hitsWithinInterval < TARGET_HITRATE_PER_INTERVAL) {
if (hitting) {
hitting = false;
} else {
hits = 0; // Reset measuring on second round, give chance to burtsy hits
}
}
hitsWithinInterval = 0;
previousLoggingTime = currentTimeMillis;
}
}
}
}
/**
* #param time
* #return long
*/
private long getLoggingInterval(long time) {
return time / LOGGING_INTERVAL;
}
/**
* #return String
*/
public static String toStringShadows() {
final ArrayList<Entry<String, Hi>> entries;
synchronized (INSTANCE_SHADOW) {
entries = Convert.toMinimumArrayList(INSTANCE_SHADOW.entrySet());
INSTANCE_SHADOW.clear();
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(new Timestamp(System.currentTimeMillis()) + " " + SHADOWED_CLASS + " Class instance instantiantion summary:\n");
stringBuilder.append("hits=" + hits + ", hitting=" + hitting + ", hitsWithinInterval=" + hitsWithinInterval + ", previousLoggingTime=" + new java.sql.Timestamp(previousLoggingTime));
if (entries.isEmpty()) {
return stringBuilder.toString();
}
Collections.sort(entries, COMPARATOR);
int index = 0;
stringBuilder.append("-----------------------------------------------------------------------");
for (Entry<String, Hi> entry : entries) {
Utils.append(stringBuilder, entry.getValue().getHitted() + "\t" + entry.getKey(), "\n");
}
for (Entry<String, Hi> entry : entries) {
final Hi hi = entry.getValue();
final StackTraceElement[] stackTrace = hi.getStackTraceElements();
final String groupName = entry.getKey();
final String threadName = hi.getThreadName();
stringBuilder.append("\n").append(++index).append('\t');
stringBuilder.append(hi.getHitted()).append("\tpcs\t").append(groupName);
stringBuilder.append("\t").append(new Timestamp(hi.getCreatedAt()).toString()).append('\t').append(threadName)
.append('\t').append(Convert.toString(stackTrace));
}
return stringBuilder.toString();
}
/**
* #param stackTrace
* #return String
*/
private static String displayStackLocaktion(final StackTraceElement[] stackTrace) {
StackTraceElement firstDistinguishingStackTraceElement = null;
for (int index = 0; index < stackTrace.length; index++) {
firstDistinguishingStackTraceElement = stackTrace[index];
if (!Arrays.asList(UNWANTED_LOCATIONS).contains(firstDistinguishingStackTraceElement.getClassName())) {
break;
}
}
StackTraceElement lastDistinguishingStackTraceElement = null;
for (int index = stackTrace.length-1; 0 <= index; index--) {
lastDistinguishingStackTraceElement = stackTrace[index];
if (lastDistinguishingStackTraceElement.getClassName().startsWith(OUR_PACKAGE_DOMAIN)) {
break;
}
}
return Utils.getPropertyPath(displayName(firstDistinguishingStackTraceElement) + "<-"
+ displayName(lastDistinguishingStackTraceElement));
}
/**
* #param firstDistinguishingStackTraceElement
* #return String
*/
private static String displayName(StackTraceElement firstDistinguishingStackTraceElement) {
return Utils.getPropertyPath(firstDistinguishingStackTraceElement.getClassName(), firstDistinguishingStackTraceElement.getMethodName(),
String.valueOf(firstDistinguishingStackTraceElement.getLineNumber()));
}
}