Added LoaderRef and MessageChunk

This commit is contained in:
Norbi Peti 2016-08-04 09:49:59 +02:00
parent 0c263d98c2
commit b01b1324eb
15 changed files with 194 additions and 61 deletions

View file

@ -10,13 +10,13 @@ import java.util.Map;
import com.google.common.io.Files; import com.google.common.io.Files;
import io.github.norbipeti.chat.server.Main; import io.github.norbipeti.chat.server.Main;
import io.github.norbipeti.chat.server.db.domain.ChatDatabaseEntity; import io.github.norbipeti.chat.server.db.domain.SavedData;
public final class DataManager { public final class DataManager {
private DataManager() { private DataManager() {
} }
public static <T extends ChatDatabaseEntity> void save(T object) { public static <T extends SavedData> void save(T object) {
try { try {
Files.write(Main.gson.toJson(object), new File(object.getClass().getName() + "-" + object.getId()), Files.write(Main.gson.toJson(object), new File(object.getClass().getName() + "-" + object.getId()),
StandardCharsets.UTF_8); StandardCharsets.UTF_8);
@ -25,11 +25,11 @@ public final class DataManager {
} }
} }
public static <T extends ChatDatabaseEntity> T load(Class<T> cl, long id) { public static <T extends SavedData> T load(Class<T> cl, long id) {
return loadFromFile(new File(cl.getName() + "-" + id), cl); return loadFromFile(new File(cl.getName() + "-" + id), cl);
} }
public static <T extends ChatDatabaseEntity> LoaderCollection<T> load(Class<T> cl) { public static <T extends SavedData> LoaderCollection<T> load(Class<T> cl) {
String[] filenames = new File(".").list(new FilenameFilter() { String[] filenames = new File(".").list(new FilenameFilter() {
@Override @Override
@ -46,9 +46,10 @@ public final class DataManager {
private static Map<File, Object> cache = new HashMap<>(); private static Map<File, Object> cache = new HashMap<>();
// TODO: Remove objects from the cache over time // TODO: Remove objects from the cache over time
// TODO: Save the object when it happens
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static <T extends ChatDatabaseEntity> T loadFromFile(File file, Class<T> cl) { private static <T extends SavedData> T loadFromFile(File file, Class<T> cl) {
try { try {
if (!file.exists()) if (!file.exists())
return cl.newInstance(); return cl.newInstance();
@ -68,13 +69,13 @@ public final class DataManager {
return null; return null;
} }
public static <T extends ChatDatabaseEntity> boolean remove(T obj) { public static <T extends SavedData> boolean remove(T obj) {
if (cache.containsValue(obj)) if (cache.containsValue(obj))
cache.values().remove(obj); cache.values().remove(obj);
return new File(obj.getClass().getName() + "-" + obj.getId()).delete(); return new File(obj.getClass().getName() + "-" + obj.getId()).delete();
} }
public static <T extends ChatDatabaseEntity> boolean remove(Class<T> cl, Long id) { public static <T extends SavedData> boolean remove(Class<T> cl, Long id) {
return new File(cl.getName() + "-" + id).delete(); return new File(cl.getName() + "-" + id).delete();
} }
} }

View file

@ -8,9 +8,21 @@ import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import io.github.norbipeti.chat.server.db.domain.ChatDatabaseEntity; import io.github.norbipeti.chat.server.db.domain.SavedData;
public class LoaderCollection<T extends ChatDatabaseEntity> implements List<T>, Serializable { /**
* <p>
* This list wil only load it's items when directly accessed
* </p>
* <p>
* And it will only save IDs of it's items
* </p>
*
* @author Norbi
*
* @param <T>
*/
public class LoaderCollection<T extends SavedData> implements List<T>, Serializable {
private static final long serialVersionUID = 5426152406394894301L; private static final long serialVersionUID = 5426152406394894301L;
List<Long> idlist; List<Long> idlist;
Class<T> cl; Class<T> cl;
@ -57,7 +69,7 @@ public class LoaderCollection<T extends ChatDatabaseEntity> implements List<T>,
@Override @Override
public boolean addAll(Collection<? extends T> c) { public boolean addAll(Collection<? extends T> c) {
return idlist.addAll(c.stream().map((data) -> { return idlist.addAll(c.stream().map((data) -> {
ChatDatabaseEntity cde = ((ChatDatabaseEntity) data); SavedData cde = ((SavedData) data);
DataManager.save(cde); DataManager.save(cde);
return cde.getId(); return cde.getId();
}).collect(Collectors.toList())); }).collect(Collectors.toList()));
@ -66,7 +78,7 @@ public class LoaderCollection<T extends ChatDatabaseEntity> implements List<T>,
@Override @Override
public boolean addAll(int index, Collection<? extends T> c) { public boolean addAll(int index, Collection<? extends T> c) {
return idlist.addAll(index, c.stream().map((data) -> { return idlist.addAll(index, c.stream().map((data) -> {
ChatDatabaseEntity cde = ((ChatDatabaseEntity) data); SavedData cde = ((SavedData) data);
DataManager.save(cde); DataManager.save(cde);
return cde.getId(); return cde.getId();
}).collect(Collectors.toList())); }).collect(Collectors.toList()));
@ -75,7 +87,7 @@ public class LoaderCollection<T extends ChatDatabaseEntity> implements List<T>,
@Override @Override
public void clear() { public void clear() {
for (Long id : idlist) for (Long id : idlist)
DataManager.remove(cl, id); //TODO: Move out to a main list DataManager.remove(cl, id); // TODO: Move out to a main list
idlist.clear(); idlist.clear();
} }
@ -127,9 +139,9 @@ 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 (SavedData.class.isAssignableFrom(o.getClass())) {
DataManager.remove((ChatDatabaseEntity) o); DataManager.remove((SavedData) o);
return idlist.remove(((ChatDatabaseEntity) o).getId()); return idlist.remove(((SavedData) o).getId());
} }
if (Long.class.isAssignableFrom(o.getClass())) if (Long.class.isAssignableFrom(o.getClass()))
DataManager.remove(cl, (Long) o); DataManager.remove(cl, (Long) o);
@ -145,8 +157,8 @@ public class LoaderCollection<T extends ChatDatabaseEntity> implements List<T>,
public boolean removeAll(Collection<?> c) { public boolean removeAll(Collection<?> c) {
boolean success = false; boolean success = false;
for (Object item : c) { for (Object item : c) {
if (ChatDatabaseEntity.class.isAssignableFrom(item.getClass())) { if (SavedData.class.isAssignableFrom(item.getClass())) {
if (idlist.remove(((ChatDatabaseEntity) item).getId())) { if (idlist.remove(((SavedData) item).getId())) {
success = true; success = true;
break; break;
} }
@ -164,8 +176,8 @@ public class LoaderCollection<T extends ChatDatabaseEntity> implements List<T>,
public boolean retainAll(Collection<?> c) { public boolean retainAll(Collection<?> c) {
List<Long> list = new ArrayList<Long>(); List<Long> list = new ArrayList<Long>();
for (Object item : c) { for (Object item : c) {
if (ChatDatabaseEntity.class.isAssignableFrom(item.getClass())) { if (SavedData.class.isAssignableFrom(item.getClass())) {
list.add(((ChatDatabaseEntity) item).getId()); list.add(((SavedData) item).getId());
} else if (Long.class.isAssignableFrom(item.getClass())) { } else if (Long.class.isAssignableFrom(item.getClass())) {
list.add((Long) item); list.add((Long) item);
} }

View file

@ -9,13 +9,13 @@ import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter; import com.google.gson.stream.JsonWriter;
import io.github.norbipeti.chat.server.db.domain.ChatDatabaseEntity; import io.github.norbipeti.chat.server.db.domain.SavedData;
//@SuppressWarnings("rawtypes") //@SuppressWarnings("rawtypes")
public class LoaderCollectionSerializer<T extends ChatDatabaseEntity> extends TypeAdapter<LoaderCollection<T>> { public class LoaderCollectionSerializer extends TypeAdapter<LoaderCollection<? extends SavedData>> {
@Override @Override
public void write(JsonWriter out, LoaderCollection<T> value) throws IOException { public void write(JsonWriter out, LoaderCollection<? extends SavedData> value) throws IOException {
out.beginObject(); out.beginObject();
out.name("items"); out.name("items");
new Gson().toJson(value.idlist, new TypeToken<List<Long>>() { new Gson().toJson(value.idlist, new TypeToken<List<Long>>() {
@ -27,7 +27,7 @@ public class LoaderCollectionSerializer<T extends ChatDatabaseEntity> extends Ty
// @SuppressWarnings("unchecked") // @SuppressWarnings("unchecked")
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public LoaderCollection<T> read(JsonReader in) throws IOException { public LoaderCollection<? extends SavedData> read(JsonReader in) throws IOException {
in.beginObject(); in.beginObject();
in.nextName(); in.nextName();
List<Long> list = new Gson().fromJson(in, new TypeToken<List<Long>>() { List<Long> list = new Gson().fromJson(in, new TypeToken<List<Long>>() {
@ -36,14 +36,15 @@ public class LoaderCollectionSerializer<T extends ChatDatabaseEntity> extends Ty
new Exception("Error: Next isn't \"class\"").printStackTrace(); new Exception("Error: Next isn't \"class\"").printStackTrace();
return null; return null;
} }
Class<T> cl; Class<? extends SavedData> cl;
try { try {
cl = (Class<T>) Class.forName(in.nextString()); cl = (Class<? extends SavedData>) Class.forName(in.nextString());
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
e.printStackTrace(); e.printStackTrace();
return null; return null;
} }
LoaderCollection<T> col = new LoaderCollection<T>(cl); // TODO LoaderCollection<? extends SavedData> col = new LoaderCollection<SavedData>(
(Class<SavedData>) cl); // TODO
col.idlist.addAll(list); col.idlist.addAll(list);
in.endObject(); in.endObject();
return col; return col;

View file

@ -1,9 +1,9 @@
package io.github.norbipeti.chat.server.data; package io.github.norbipeti.chat.server.data;
import java.util.Iterator; import java.util.Iterator;
import io.github.norbipeti.chat.server.db.domain.ChatDatabaseEntity; import io.github.norbipeti.chat.server.db.domain.SavedData;
public final class LoaderIterator<T extends ChatDatabaseEntity> implements Iterator<T> { public final class LoaderIterator<T extends SavedData> implements Iterator<T> {
private Iterator<Long> iterator; private Iterator<Long> iterator;
private T lastitem; private T lastitem;
private Class<T> cl; private Class<T> cl;

View file

@ -2,9 +2,9 @@ package io.github.norbipeti.chat.server.data;
import java.util.ListIterator; import java.util.ListIterator;
import io.github.norbipeti.chat.server.db.domain.ChatDatabaseEntity; import io.github.norbipeti.chat.server.db.domain.SavedData;
public final class LoaderListIterator<T extends ChatDatabaseEntity> implements ListIterator<T> { public final class LoaderListIterator<T extends SavedData> implements ListIterator<T> {
private ListIterator<Long> listiterator; private ListIterator<Long> listiterator;
private T lastitem; private T lastitem;
private Class<T> cl; private Class<T> cl;

View file

@ -0,0 +1,37 @@
package io.github.norbipeti.chat.server.data;
import io.github.norbipeti.chat.server.db.domain.SavedData;
/**
* <p>
* This class will only store IDs and load the object when calling
* {@link #get()}
* </p>
* <p>
* And will also only save IDs when serialized with {@link LoaderRefSerializer}
* </p>
*
* @author Norbi
*
* @param <T>
* The type of the stored object
*/
public class LoaderRef<T extends SavedData> {
Class<T> cl;
Long id;
public LoaderRef(Class<T> cl, Long id) {
this.id = id;
this.cl = cl;
}
@SuppressWarnings("unchecked")
public LoaderRef(T obj) {
this.id = obj.getId();
this.cl = (Class<T>) obj.getClass();
}
public T get() {
return DataManager.load(cl, id);
}
}

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.SavedData;
//@SuppressWarnings("rawtypes")
public class LoaderRefSerializer extends TypeAdapter<LoaderRef<? extends SavedData>> {
@Override
public void write(JsonWriter out, LoaderRef<? extends SavedData> value) throws IOException {
out.beginObject();
out.name("items");
new Gson().toJson(value.id, new TypeToken<List<Long>>() {
}.getType(), out);
out.name("class").value(value.cl.getName());
out.endObject();
}
// @SuppressWarnings("unchecked")
@SuppressWarnings("unchecked")
@Override
public LoaderRef<? extends SavedData> read(JsonReader in) throws IOException {
in.beginObject();
in.nextName();
Long id = new Gson().fromJson(in, new TypeToken<Long>() {
}.getType());
if (!in.nextName().equals("class")) {
new Exception("Error: Next isn't \"class\"").printStackTrace();
return null;
}
Class<? extends SavedData> cl;
try {
cl = (Class<? extends SavedData>) Class.forName(in.nextString());
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
LoaderRef<? extends SavedData> col = new LoaderRef<SavedData>(
(Class<SavedData>) cl, id); // TODO
in.endObject();
return col;
}
}

View file

@ -20,7 +20,7 @@ public class DataProvider implements AutoCloseable {
em.getTransaction().begin(); em.getTransaction().begin();
} }
public <T extends ChatDatabaseEntity> T save(T object) { public <T extends SavedData> T save(T object) {
T obj = em.merge(object); T obj = em.merge(object);
return obj; return obj;
} }

View file

@ -6,7 +6,7 @@ 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 SavedData {
private static final long serialVersionUID = 5058682475353799722L; private static final long serialVersionUID = 5058682475353799722L;
// @Id // @Id
// @GeneratedValue(strategy = GenerationType.IDENTITY) // @GeneratedValue(strategy = GenerationType.IDENTITY)
@ -14,7 +14,7 @@ public class Conversation extends ChatDatabaseEntity {
// 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 LoaderCollection<Message> messsages = new LoaderCollection<>(Message.class); private LoaderCollection<MessageChunk> messsagechunks = new LoaderCollection<>(MessageChunk.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 =
@ -28,12 +28,12 @@ public class Conversation extends ChatDatabaseEntity {
// @JoinTable(name = "User_Conversation") // @JoinTable(name = "User_Conversation")
private LoaderCollection<User> users = new LoaderCollection<>(User.class); private LoaderCollection<User> users = new LoaderCollection<>(User.class);
public LoaderCollection<Message> getMesssages() { public LoaderCollection<MessageChunk> getMesssageChunks() {
return messsages; return messsagechunks;
} }
public void setMesssages(LoaderCollection<Message> messsages) { public void setMesssageChunks(LoaderCollection<MessageChunk> messsages) {
this.messsages = messsages; this.messsagechunks = messsages;
} }
public LoaderCollection<User> getUsers() { public LoaderCollection<User> getUsers() {

View file

@ -1,17 +1,20 @@
package io.github.norbipeti.chat.server.db.domain; package io.github.norbipeti.chat.server.db.domain;
import java.io.Serializable;
import java.util.Date; import java.util.Date;
import javax.persistence.*; import javax.persistence.*;
import io.github.norbipeti.chat.server.data.LoaderRef;
@Entity @Entity
@Table(name = "MESSAGE") @Table(name = "MESSAGE")
public class Message extends ChatDatabaseEntity { public class Message implements Serializable {
private static final long serialVersionUID = 6345941601716826570L; private static final long serialVersionUID = 6345941601716826570L;
//@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;
@ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) @ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
// @JoinTable(name="user_message") // @JoinTable(name="user_message")
private User sender; private User sender;
@ -19,7 +22,7 @@ public class Message extends ChatDatabaseEntity {
private String message; private String message;
@ManyToOne(fetch = FetchType.EAGER) @ManyToOne(fetch = FetchType.EAGER)
// @JoinTable(name="conversation_message") // @JoinTable(name="conversation_message")
private Conversation conversation; private LoaderRef<MessageChunk> messagechunk;
public User getSender() { public User getSender() {
return sender; return sender;
@ -45,19 +48,17 @@ public class Message extends ChatDatabaseEntity {
this.message = message; this.message = message;
} }
public Conversation getConversation() { public LoaderRef<MessageChunk> getMessageChunk() {
return conversation; return messagechunk;
} }
public void setConversation(Conversation conversation) { public void setMessageChunk(LoaderRef<MessageChunk> messagechunk) {
this.conversation = conversation; this.messagechunk = messagechunk;
} }
/*public Long getId() { /*
return id; * public Long getId() { return id; }
} *
* public void setId(Long id) { this.id = id; }
public void setId(Long id) { */
this.id = id;
}*/
} }

View file

@ -0,0 +1,29 @@
package io.github.norbipeti.chat.server.db.domain;
import java.util.ArrayList;
import java.util.List;
import io.github.norbipeti.chat.server.data.LoaderRef;
public class MessageChunk extends SavedData {
private static final long serialVersionUID = -1665300779209348467L;
private List<Message> messages = new ArrayList<>();
private LoaderRef<Conversation> conversation;
public List<Message> getMessages() {
return messages;
}
public void setMessages(List<Message> messages) {
this.messages = messages;
}
public LoaderRef<Conversation> getConversation() {
return conversation;
}
public void setConversation(LoaderRef<Conversation> conversation) {
this.conversation = conversation;
}
}

View file

@ -3,7 +3,7 @@ package io.github.norbipeti.chat.server.db.domain;
import java.io.Serializable; import java.io.Serializable;
@SuppressWarnings("serial") @SuppressWarnings("serial")
public abstract class ChatDatabaseEntity implements Serializable { public abstract class SavedData implements Serializable {
private static long nextID = 0; private static long nextID = 0;
private long id; private long id;
@ -12,7 +12,7 @@ public abstract class ChatDatabaseEntity implements Serializable {
return id; return id;
} }
protected ChatDatabaseEntity() { protected SavedData() {
id = nextID++; id = nextID++;
} }
} }

View file

@ -9,7 +9,7 @@ import io.github.norbipeti.chat.server.data.LoaderCollection;
@Entity @Entity
@Table(name = "\"USER\"") @Table(name = "\"USER\"")
public class User extends ChatDatabaseEntity { public class User extends SavedData {
private static final long serialVersionUID = 2862762084164225666L; private static final long serialVersionUID = 2862762084164225666L;
// @Id // @Id
// @GeneratedValue(strategy = GenerationType.IDENTITY) // @GeneratedValue(strategy = GenerationType.IDENTITY)

View file

@ -45,8 +45,8 @@ public class IndexPage extends Page {
cide.attr("style", "display: none"); cide.attr("style", "display: none");
cide.attr("id", "convidp"); cide.attr("id", "convidp");
cide.text(Long.toString(conv.getId())); cide.text(Long.toString(conv.getId()));
LogManager.getLogger().log(Level.INFO, "Messages: " + conv.getMesssages().size()); LogManager.getLogger().log(Level.INFO, "Messages: " + conv.getMesssageChunks().size());
for (Message message : conv.getMesssages()) { for (Message message : conv.getMesssageChunks()) {
Element msgelement = channelmessages.appendElement("div"); //TODO: Save messages in conversation files Element msgelement = channelmessages.appendElement("div"); //TODO: Save messages in conversation files
Element header = msgelement.appendElement("p"); Element header = msgelement.appendElement("p");
header.text(message.getSender().getName() + " - " + message.getTime()); header.text(message.getSender().getName() + " - " + message.getTime());

View file

@ -72,9 +72,9 @@ public class MessageAjaxPage extends Page {
msg.setTime(new Date()); msg.setTime(new Date());
msg.setConversation(conv); // TODO: Store relations at one side or both msg.setConversation(conv); // TODO: Store relations at one side or both
DataManager.save(msg); DataManager.save(msg);
conv.getMesssages().add(msg); conv.getMesssageChunks().add(msg);
DataManager.save(conv); DataManager.save(conv);
LogManager.getLogger().log(Level.DEBUG, "Added conversation's message count: " + conv.getMesssages().size()); LogManager.getLogger().log(Level.DEBUG, "Added conversation's message count: " + conv.getMesssageChunks().size());
IOHelper.SendResponse(200, "Success", exchange); IOHelper.SendResponse(200, "Success", exchange);
} }