Fixed LoaderCollection serialization

This commit is contained in:
Norbi Peti 2016-08-03 12:55:23 +02:00
parent 8068b292cf
commit 3a1f2a65e1
8 changed files with 125 additions and 95 deletions

View file

@ -4,6 +4,7 @@ import java.lang.reflect.Modifier;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.Set; import java.util.Set;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
@ -12,18 +13,34 @@ import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner; import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper; import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder; import org.reflections.util.ConfigurationBuilder;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpServer;
import io.github.norbipeti.chat.server.data.DataManager; import io.github.norbipeti.chat.server.data.DataManager;
import io.github.norbipeti.chat.server.data.LoaderCollection;
import io.github.norbipeti.chat.server.data.LoaderCollectionSerializer;
import io.github.norbipeti.chat.server.db.domain.*; import io.github.norbipeti.chat.server.db.domain.*;
import io.github.norbipeti.chat.server.page.*; import io.github.norbipeti.chat.server.page.*;
public class Main { public class Main {
public static Gson gson;
public static void main(String[] args) { // http://stackoverflow.com/questions/9266632/access-restriction-is-not-accessible-due-to-restriction-on-required-library/10642163#10642163 public static void main(String[] args) { // http://stackoverflow.com/questions/9266632/access-restriction-is-not-accessible-due-to-restriction-on-required-library/10642163#10642163
try { // rt.jar Javadoc: try { // rt.jar Javadoc:
// https://docs.oracle.com/javase/8/docs/jre/api/net/httpserver/spec/ // https://docs.oracle.com/javase/8/docs/jre/api/net/httpserver/spec/
// https://docs.oracle.com/javase/8/docs/api/ // https://docs.oracle.com/javase/8/docs/api/
LogManager.getLogger().log(Level.INFO, "Loading database..."); LogManager.getLogger().log(Level.INFO, "Loading files...");
final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(new TypeToken<LoaderCollection<Conversation>>() {
}.getType(), new LoaderCollectionSerializer<Conversation>());
gsonBuilder.registerTypeAdapter(new TypeToken<LoaderCollection<Message>>() {
}.getType(), new LoaderCollectionSerializer<Message>());
gsonBuilder.registerTypeAdapter(new TypeToken<LoaderCollection<User>>() {
}.getType(), new LoaderCollectionSerializer<User>());
gson = gsonBuilder.create();
User user = new User(); User user = new User();
user.setName("asd"); user.setName("asd");
user.setEmail("test@test.com"); user.setEmail("test@test.com");

View file

@ -6,7 +6,7 @@ import java.io.FilenameFilter;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import com.google.common.io.Files; import com.google.common.io.Files;
import com.google.gson.Gson; import io.github.norbipeti.chat.server.Main;
import io.github.norbipeti.chat.server.db.domain.ChatDatabaseEntity; import io.github.norbipeti.chat.server.db.domain.ChatDatabaseEntity;
public final class DataManager { public final class DataManager {
@ -14,8 +14,7 @@ public final class DataManager {
} }
public static <T extends ChatDatabaseEntity> void save(T object) throws IOException { public static <T extends ChatDatabaseEntity> void save(T object) throws IOException {
Gson gson = new Gson(); Files.write(Main.gson.toJson(object), new File(object.getClass().getName() + "-" + object.getId()),
Files.write(gson.toJson(object), new File(object.getClass().getName() + "-" + object.getId()),
StandardCharsets.UTF_8); StandardCharsets.UTF_8);
} }
@ -47,8 +46,7 @@ public final class DataManager {
String line; String line;
while ((line = reader.readLine()) != null) while ((line = reader.readLine()) != null)
objstr += line; objstr += line;
Gson gson = new Gson(); return Main.gson.fromJson(objstr, cl);
return gson.fromJson(objstr, cl);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }

View file

@ -12,9 +12,8 @@ import io.github.norbipeti.chat.server.db.domain.ChatDatabaseEntity;
public class LoaderCollection<T extends ChatDatabaseEntity> implements List<T>, Serializable { public class LoaderCollection<T extends ChatDatabaseEntity> implements List<T>, Serializable {
private static final long serialVersionUID = 5426152406394894301L; private static final long serialVersionUID = 5426152406394894301L;
private List<Long> contacts; List<Long> idlist;
private Class<T> cl; Class<T> cl;
private transient boolean forsave = false;
/** /**
* Only used for serialization * Only used for serialization
@ -25,92 +24,89 @@ public class LoaderCollection<T extends ChatDatabaseEntity> implements List<T>,
public LoaderCollection(Class<T> cl) { public LoaderCollection(Class<T> cl) {
this.cl = cl; this.cl = cl;
contacts = new ArrayList<>(); idlist = new ArrayList<>();
} }
public LoaderCollection(LoaderCollection<T> parentofsub, int fromIndex, int toIndex) { public LoaderCollection(LoaderCollection<T> parentofsub, int fromIndex, int toIndex) {
this.cl = parentofsub.cl; this.cl = parentofsub.cl;
contacts = parentofsub.contacts.subList(fromIndex, toIndex); idlist = parentofsub.idlist.subList(fromIndex, toIndex);
} }
public LoaderCollection(Class<T> cl, int capacity) { public LoaderCollection(Class<T> cl, int capacity) {
this.cl = cl; this.cl = cl;
contacts = new ArrayList<>(capacity); idlist = new ArrayList<>(capacity);
} }
@Override @Override
public Iterator<T> iterator() { public Iterator<T> iterator() {
if (forsave) return new LoaderIterator<T>(idlist.iterator(), cl);
return contacts.iterator(); // TODO: Fix
else
return new LoaderIterator<T>(contacts.iterator(), cl);
} }
@Override @Override
public boolean add(T e) { public boolean add(T e) {
return contacts.add(e.getId()); return idlist.add(e.getId());
} }
@Override @Override
public void add(int index, T element) { public void add(int index, T element) {
contacts.add(index, element.getId()); idlist.add(index, element.getId());
} }
@Override @Override
public boolean addAll(Collection<? extends T> c) { public boolean addAll(Collection<? extends T> c) {
return contacts return idlist
.addAll(c.stream().map((data) -> ((ChatDatabaseEntity) data).getId()).collect(Collectors.toList())); .addAll(c.stream().map((data) -> ((ChatDatabaseEntity) data).getId()).collect(Collectors.toList()));
} }
@Override @Override
public boolean addAll(int index, Collection<? extends T> c) { public boolean addAll(int index, Collection<? extends T> c) {
return contacts.addAll(index, return idlist.addAll(index,
c.stream().map((data) -> ((ChatDatabaseEntity) data).getId()).collect(Collectors.toList())); c.stream().map((data) -> ((ChatDatabaseEntity) data).getId()).collect(Collectors.toList()));
} }
@Override @Override
public void clear() { public void clear() {
contacts.clear(); idlist.clear();
} }
@Override @Override
public boolean contains(Object o) { public boolean contains(Object o) {
return contacts.contains(o); return idlist.contains(o);
} }
@Override @Override
public boolean containsAll(Collection<?> c) { public boolean containsAll(Collection<?> c) {
return contacts.containsAll(c); return idlist.containsAll(c);
} }
@Override @Override
public T get(int index) { public T get(int index) {
return DataManager.load(cl, contacts.get(index)); return DataManager.load(cl, idlist.get(index));
} }
@Override @Override
public int indexOf(Object o) { public int indexOf(Object o) {
return contacts.indexOf(o); return idlist.indexOf(o);
} }
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
return contacts.isEmpty(); return idlist.isEmpty();
} }
@Override @Override
public int lastIndexOf(Object o) { public int lastIndexOf(Object o) {
return contacts.lastIndexOf(o); return idlist.lastIndexOf(o);
} }
@Override @Override
public ListIterator<T> listIterator() { public ListIterator<T> listIterator() {
return new LoaderListIterator<T>(contacts.listIterator(), cl); return new LoaderListIterator<T>(idlist.listIterator(), cl);
} }
@Override @Override
public ListIterator<T> listIterator(int index) { public ListIterator<T> listIterator(int index) {
return new LoaderListIterator<T>(contacts.listIterator(index), cl); return new LoaderListIterator<T>(idlist.listIterator(index), cl);
} }
/** /**
@ -122,13 +118,13 @@ public class LoaderCollection<T extends ChatDatabaseEntity> implements List<T>,
@Override @Override
public boolean remove(Object o) { public boolean remove(Object o) {
if (ChatDatabaseEntity.class.isAssignableFrom(o.getClass())) if (ChatDatabaseEntity.class.isAssignableFrom(o.getClass()))
return contacts.remove(((ChatDatabaseEntity) o).getId()); return idlist.remove(((ChatDatabaseEntity) o).getId());
return contacts.remove(o); return idlist.remove(o);
} }
@Override @Override
public T remove(int index) { public T remove(int index) {
return DataManager.load(cl, contacts.remove(index)); return DataManager.load(cl, idlist.remove(index));
} }
@Override @Override
@ -136,12 +132,12 @@ public class LoaderCollection<T extends ChatDatabaseEntity> implements List<T>,
boolean success = false; boolean success = false;
for (Object item : c) { for (Object item : c) {
if (ChatDatabaseEntity.class.isAssignableFrom(item.getClass())) { if (ChatDatabaseEntity.class.isAssignableFrom(item.getClass())) {
if (contacts.remove(((ChatDatabaseEntity) item).getId())) { if (idlist.remove(((ChatDatabaseEntity) item).getId())) {
success = true; success = true;
break; break;
} }
} else { } else {
if (contacts.remove(item)) { if (idlist.remove(item)) {
success = true; success = true;
break; break;
} }
@ -160,17 +156,17 @@ public class LoaderCollection<T extends ChatDatabaseEntity> implements List<T>,
list.add((Long) item); list.add((Long) item);
} }
} }
return contacts.retainAll(list); return idlist.retainAll(list);
} }
@Override @Override
public T set(int index, T element) { public T set(int index, T element) {
return DataManager.load(cl, contacts.set(index, element.getId())); return DataManager.load(cl, idlist.set(index, element.getId()));
} }
@Override @Override
public int size() { public int size() {
return contacts.size(); return idlist.size();
} }
@Override @Override
@ -181,14 +177,14 @@ public class LoaderCollection<T extends ChatDatabaseEntity> implements List<T>,
@Override @Override
public Object[] toArray() { public Object[] toArray() {
return contacts.stream().map((data) -> { return idlist.stream().map((data) -> {
return DataManager.load(cl, data); return DataManager.load(cl, data);
}).collect(Collectors.toList()).toArray(); }).collect(Collectors.toList()).toArray();
} }
@Override @Override
public <U> U[] toArray(U[] a) { public <U> U[] toArray(U[] a) {
return contacts.stream().map((data) -> { return idlist.stream().map((data) -> {
return DataManager.load(cl, data); return DataManager.load(cl, data);
}).collect(Collectors.toList()).toArray(a); }).collect(Collectors.toList()).toArray(a);
} }
@ -200,7 +196,7 @@ public class LoaderCollection<T extends ChatDatabaseEntity> implements List<T>,
public String toString(boolean loaditems) { public String toString(boolean loaditems) {
StringBuilder sb = new StringBuilder("["); StringBuilder sb = new StringBuilder("[");
for (Long item : contacts) { for (Long item : idlist) {
if (loaditems) if (loaditems)
sb.append(DataManager.load(cl, item)); sb.append(DataManager.load(cl, item));
else else
@ -209,12 +205,4 @@ public class LoaderCollection<T extends ChatDatabaseEntity> implements List<T>,
sb.append("]"); sb.append("]");
return sb.toString(); return sb.toString();
} }
public boolean isForsave() {
return forsave;
}
public void setForsave(boolean forsave) {
this.forsave = forsave;
}
} }

View file

@ -0,0 +1,52 @@
package io.github.norbipeti.chat.server.data;
import java.io.IOException;
import java.util.List;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import io.github.norbipeti.chat.server.db.domain.ChatDatabaseEntity;
//@SuppressWarnings("rawtypes")
public class LoaderCollectionSerializer<T extends ChatDatabaseEntity> extends TypeAdapter<LoaderCollection<T>> {
@Override
public void write(JsonWriter out, LoaderCollection<T> value) throws IOException {
out.beginObject();
out.name("items");
new Gson().toJson(value.idlist, new TypeToken<List<Long>>() {
}.getType(), out);
out.name("class").value(value.cl.getName());
out.endObject();
}
// @SuppressWarnings("unchecked")
@SuppressWarnings("unchecked")
@Override
public LoaderCollection<T> read(JsonReader in) throws IOException {
in.beginObject();
in.nextName();
List<Long> list = new Gson().fromJson(in, new TypeToken<List<Long>>() {
}.getType());
if (!in.nextName().equals("class")) {
new Exception("Error: Next isn't \"class\"").printStackTrace();
return null;
}
Class<T> cl;
try {
cl = (Class<T>) Class.forName(in.nextString());
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
LoaderCollection<T> col = new LoaderCollection<T>(cl); // TODO
col.idlist.addAll(list);
in.endObject();
return col;
}
}

View file

@ -1,23 +1,20 @@
package io.github.norbipeti.chat.server.db.domain; package io.github.norbipeti.chat.server.db.domain;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.*; import javax.persistence.*;
import io.github.norbipeti.chat.server.data.LoaderCollection;
@Entity @Entity
@Table(name = "CONVERSATION") @Table(name = "CONVERSATION")
public class Conversation extends ChatDatabaseEntity { public class Conversation extends ChatDatabaseEntity {
private static final long serialVersionUID = 5058682475353799722L; private static final long serialVersionUID = 5058682475353799722L;
//@Id // @Id
//@GeneratedValue(strategy = GenerationType.IDENTITY) // @GeneratedValue(strategy = GenerationType.IDENTITY)
//@Column(name = "ID", unique = true, nullable = false) // @Column(name = "ID", unique = true, nullable = false)
//private Long id; // private Long id;
@ElementCollection(fetch = FetchType.EAGER) @ElementCollection(fetch = FetchType.EAGER)
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "conversation") @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "conversation")
private List<Message> messsages; private LoaderCollection<Message> messsages = new LoaderCollection<>(Message.class);
@ElementCollection(fetch = FetchType.EAGER) @ElementCollection(fetch = FetchType.EAGER)
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
// @JoinTable(name = "User_Conversation", joinColumns = @JoinColumn(name = // @JoinTable(name = "User_Conversation", joinColumns = @JoinColumn(name =
@ -29,33 +26,23 @@ public class Conversation extends ChatDatabaseEntity {
// "conversation_id", referencedColumnName = "id"), inverseJoinColumns = // "conversation_id", referencedColumnName = "id"), inverseJoinColumns =
// @JoinColumn(name = "user_id", referencedColumnName = "id")) // @JoinColumn(name = "user_id", referencedColumnName = "id"))
// @JoinTable(name = "User_Conversation") // @JoinTable(name = "User_Conversation")
private Set<User> users; private LoaderCollection<User> users = new LoaderCollection<>(User.class);
public List<Message> getMesssages() { public LoaderCollection<Message> getMesssages() {
if (messsages == null)
messsages = new ArrayList<>();
return messsages; return messsages;
} }
public void setMesssages(List<Message> messsages) { public void setMesssages(LoaderCollection<Message> messsages) {
this.messsages = messsages; this.messsages = messsages;
} }
public Set<User> getUsers() { public LoaderCollection<User> getUsers() {
if (users == null)
users = new HashSet<>();
return users; return users;
} }
public void setUsers(Set<User> users) { /*
this.users = users; * public Long getId() { return id; }
} *
* public void setId(Long id) { this.id = id; }
/*public Long getId() { */
return id;
}
public void setId(Long id) {
this.id = id;
}*/
} }

View file

@ -1,10 +1,7 @@
package io.github.norbipeti.chat.server.db.domain; package io.github.norbipeti.chat.server.db.domain;
import java.io.IOException; import java.io.IOException;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import javax.persistence.*; import javax.persistence.*;
import io.github.norbipeti.chat.server.data.DataManager; import io.github.norbipeti.chat.server.data.DataManager;
@ -23,7 +20,7 @@ public class User extends ChatDatabaseEntity {
private String password; private String password;
@ElementCollection(fetch = FetchType.EAGER) @ElementCollection(fetch = FetchType.EAGER)
@OneToOne(cascade = CascadeType.ALL) @OneToOne(cascade = CascadeType.ALL)
private LoaderCollection<User> contacts; private LoaderCollection<User> contacts = new LoaderCollection<User>(User.class);
private String salt; private String salt;
// @Column(columnDefinition = "CHAR(16) FOR BIT DATA") // @Column(columnDefinition = "CHAR(16) FOR BIT DATA")
@Column(columnDefinition = "VARCHAR(64)") @Column(columnDefinition = "VARCHAR(64)")
@ -36,7 +33,7 @@ public class User extends ChatDatabaseEntity {
// @JoinColumn(referencedColumnName = "id", unique = false), // @JoinColumn(referencedColumnName = "id", unique = false),
// inverseJoinColumns = @JoinColumn(referencedColumnName = "id", unique = // inverseJoinColumns = @JoinColumn(referencedColumnName = "id", unique =
// false)) // false))
private Set<Conversation> conversations; private LoaderCollection<Conversation> conversations = new LoaderCollection<>(Conversation.class);
/** /**
* Loads all contact data * Loads all contact data
@ -45,8 +42,6 @@ public class User extends ChatDatabaseEntity {
* @throws IOException * @throws IOException
*/ */
public List<User> getContacts() throws IOException { public List<User> getContacts() throws IOException {
if (contacts == null)
contacts = new LoaderCollection<User>(User.class);
return contacts; return contacts;
} }
@ -96,16 +91,10 @@ public class User extends ChatDatabaseEntity {
this.sessionid = sessionid; this.sessionid = sessionid;
} }
public Set<Conversation> getConversations() { public LoaderCollection<Conversation> getConversations() {
if (conversations == null)
conversations = new HashSet<>();
return conversations; return conversations;
} }
public void setConversations(Set<Conversation> conversations) {
this.conversations = conversations;
}
public User() { public User() {
} }

View file

@ -37,7 +37,7 @@ public class IndexPage extends Page {
Element channelmessages = doc.getElementById("channelmessages"); Element channelmessages = doc.getElementById("channelmessages");
LogManager.getLogger().log(Level.INFO, "Conversations: " + DataManager.load(Conversation.class).size()); LogManager.getLogger().log(Level.INFO, "Conversations: " + DataManager.load(Conversation.class).size());
LogManager.getLogger().log(Level.INFO, "User conversations: " + user.getConversations().size()); LogManager.getLogger().log(Level.INFO, "User conversations: " + user.getConversations().size());
Conversation convo = user.getConversations().iterator().next(); Conversation convo = user.getConversations().get(0);
LogManager.getLogger().log(Level.INFO, "Messages: " + convo.getMesssages().size()); LogManager.getLogger().log(Level.INFO, "Messages: " + convo.getMesssages().size());
for (Message message : convo.getMesssages()) { for (Message message : convo.getMesssages()) {
Element msgelement = channelmessages.appendElement("div"); Element msgelement = channelmessages.appendElement("div");

View file

@ -2,8 +2,6 @@ package io.github.norbipeti.chat.server.page;
import java.io.IOException; import java.io.IOException;
import java.util.Date; import java.util.Date;
import java.util.Set;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
@ -11,6 +9,7 @@ import com.sun.net.httpserver.HttpExchange;
import io.github.norbipeti.chat.server.IOHelper; import io.github.norbipeti.chat.server.IOHelper;
import io.github.norbipeti.chat.server.data.DataManager; import io.github.norbipeti.chat.server.data.DataManager;
import io.github.norbipeti.chat.server.data.LoaderCollection;
import io.github.norbipeti.chat.server.db.domain.Conversation; import io.github.norbipeti.chat.server.db.domain.Conversation;
import io.github.norbipeti.chat.server.db.domain.Message; import io.github.norbipeti.chat.server.db.domain.Message;
import io.github.norbipeti.chat.server.db.domain.User; import io.github.norbipeti.chat.server.db.domain.User;
@ -49,7 +48,7 @@ public class MessageAjaxPage extends Page {
IOHelper.SendResponse(400, "<h1>400 Bad request</h1><p>The message cannot be empty,</p>", exchange); IOHelper.SendResponse(400, "<h1>400 Bad request</h1><p>The message cannot be empty,</p>", exchange);
return; return;
} }
Set<Conversation> convos = user.getConversations(); LoaderCollection<Conversation> convos = user.getConversations();
Conversation conv = null; Conversation conv = null;
LogManager.getLogger().log(Level.DEBUG, "Len: " + convos.size()); LogManager.getLogger().log(Level.DEBUG, "Len: " + convos.size());
for (Conversation con : convos) { for (Conversation con : convos) {