This repository has been archived on 2021-12-05. You can view files and clone it, but cannot push or open issues or pull requests.
envoy/server/src/main/java/envoy/server/data/PersistenceManager.java

336 lines
11 KiB
Java
Raw Normal View History

package envoy.server.data;
2020-01-03 16:21:35 +01:00
import java.time.Instant;
import java.util.List;
import java.util.logging.Level;
import javax.persistence.*;
import envoy.data.User.UserStatus;
import envoy.server.net.ConnectionManager;
import envoy.util.EnvoyLog;
2020-01-03 16:21:35 +01:00
/**
* Contains operations used for persistence.
*
2020-01-03 16:21:35 +01:00
* @author Leon Hofmeister
* @author Maximilian Käfer
2020-01-03 16:21:35 +01:00
* @since Envoy Server Standalone v0.1-alpha
*/
public final class PersistenceManager {
2020-01-03 16:21:35 +01:00
private final EntityManager entityManager = Persistence.createEntityManagerFactory("envoy").createEntityManager();
private final EntityTransaction transaction = entityManager.getTransaction();
private static final PersistenceManager persistenceManager = new PersistenceManager();
/**
* Creates the singleton instance of the @link{PersistenceManager}.
*
* @since Envoy Server Standalone v0.1-alpha
*/
private PersistenceManager() {
Runtime.getRuntime().addShutdownHook(new Thread(() -> transaction(() -> {
ConnectionManager.getInstance()
.getOnlineUsers()
.stream()
.map(this::getUserByID)
.forEach(user -> { user.setStatus(UserStatus.OFFLINE); user.setLastSeen(Instant.now()); entityManager.merge(user); });
})));
}
/**
* @return the {@link PersistenceManager} singleton
* @since Envoy Server Standalone v0.1-alpha
*/
2020-02-12 07:10:33 +01:00
public static PersistenceManager getInstance() { return persistenceManager; }
/**
* Adds a {@link Contact} to the database.
*
* @param contact the {@link Contact} to add to the database
* @since Envoy Server Standalone v0.1-alpha
*/
public void addContact(Contact contact) { persist(contact); }
/**
* Adds a {@link Message} to the database.
*
* @param message the {@link Message} to add to the database
* @since Envoy Server Standalone v0.1-alpha
*/
public void addMessage(Message message) { persist(message); }
/**
* Adds a {@link ConfigItem} to the database.
*
* @param configItem the {@link ConfigItem} to add to the database
* @since Envoy Server Standalone v0.1-alpha
*/
public void addConfigItem(ConfigItem configItem) { persist(configItem); }
/**
* Updates a {@link Contact} in the database
*
* @param contact the {@link Contact} to add to the database
* @since Envoy Server Standalone v0.1-alpha
*/
public void updateContact(Contact contact) { merge(contact); }
/**
* Updates a {@link Message} in the database.
*
* @param message the message to update
* @since Envoy Server Standalone v0.1-alpha
*/
public void updateMessage(Message message) { merge(message); }
/**
* Updates a {@link ConfigItem} in the database.
*
* @param configItem the configItem to update
* @since Envoy Server Standalone v0.1-alpha
*/
public void updateConfigItem(ConfigItem configItem) { merge(configItem); }
/**
* Deletes a {@link Contact} in the database.
*
* @param contact the {@link Contact} to delete
* @since Envoy Server Standalone v0.1-alpha
*/
2020-10-10 22:25:39 +02:00
public void deleteContact(Contact contact) {
transaction(() -> {
// Remove this contact from the contact list of his contacts
for (final var remainingContact : contact.getContacts())
remainingContact.getContacts().remove(contact);
});
remove(contact);
}
/**
* Deletes a {@link Message} in the database.
*
* @param message the {@link Message} to delete
* @since Envoy Server Standalone v0.1-alpha
*/
public void deleteMessage(Message message) { remove(message); }
/**
* Searches for a {@link User} with a specific ID.
*
* @param id the id to search for
* @return the user with the specified ID or {@code null} if none was found
* @since Envoy Server Standalone v0.1-alpha
*/
public User getUserByID(long id) { return entityManager.find(User.class, id); }
/**
* Searches for a {@link Group} with a specific ID.
*
* @param id the id to search for
* @return the group with the specified ID or {@code null} if none was found
* @since Envoy Server Standalone v0.1-beta
*/
public Group getGroupByID(long id) { return entityManager.find(Group.class, id); }
/**
* Searches for a {@link Contact} with a specific ID.
*
* @param id the id to search for
* @return the contact with the specified ID or {@code null} if none was found
* @since Envoy Server Standalone v0.1-beta
*/
public Contact getContactByID(long id) { return entityManager.find(Contact.class, id); }
/**
* Searched for a {@link User} with a specific name.
*
* @param name the name of the user
* @return the user with the specified name
* @since Envoy Server Standalone v0.1-alpha
*/
public User getUserByName(String name) {
return (User) entityManager.createNamedQuery(User.findByName).setParameter("name", name).getSingleResult();
}
/**
* Searched for a {@link Group} with a specific name.
*
* @param name the name of the group
* @return the group with the specified name
* @since Envoy Server Standalone v0.1-alpha
*/
public Group getGroupByName(String name) {
return (Group) entityManager.createNamedQuery(Group.findByName).setParameter("name", name).getSingleResult();
}
/**
* Searches for a {@link Message} with a specific id.
*
* @param id the id to search for
* @return the message with the specified ID or {@code null} if none is found
* @since Envoy Server Standalone v0.1-alpha
*/
public Message getMessageByID(long id) { return entityManager.find(Message.class, id); }
/**
* @param key the name of this {@link ConfigItem}
* @return the {@link ConfigItem} with the given name
* @since Envoy Server Standalone v0.1-alpha
*/
public ConfigItem getConfigItemByID(String key) { return entityManager.find(ConfigItem.class, key); }
/**
* Returns all messages received while being offline or the ones that have
* changed.
*
* @param user the user who wants to receive his unread messages
* @param lastSync the time stamp of the last synchronization
* @return all messages that the client does not yet have (unread messages)
* @since Envoy Server Standalone v0.2-beta
*/
public List<Message> getPendingMessages(User user, Instant lastSync) {
return entityManager.createNamedQuery(Message.getPending).setParameter("user", user).setParameter("lastSeen", lastSync).getResultList();
}
/**
* Returns all groupMessages received while being offline or the ones that have
* changed.
*
* @param user the user who wants to receive his unread groupMessages
* @param lastSync the time stamp of the last synchronization
* @return all groupMessages that the client does not yet have (unread
* groupMessages)
* @since Envoy Server Standalone v0.2-beta
*/
public List<GroupMessage> getPendingGroupMessages(User user, Instant lastSync) {
return entityManager.createNamedQuery(GroupMessage.getPendingGroupMsg)
.setParameter("userId", user.getID())
.setParameter("lastSeen", lastSync)
.getResultList();
}
/**
* Searches for users matching a search phrase. Contacts of the attached user
* and the attached user is ignored.
*
* @param searchPhrase the search phrase
* @param userId the ID of the user in whose context the search is
* performed
* @return a list of all users who matched the criteria
* @since Envoy Server Standalone v0.1-alpha
*/
public List<User> searchUsers(String searchPhrase, long userId) {
return entityManager.createNamedQuery(User.searchByName)
.setParameter("searchPhrase", searchPhrase + "%")
.setParameter("context", getUserByID(userId))
.getResultList();
2020-02-08 13:53:58 +01:00
}
/**
* Adds a contact to the contact list of another contact and vice versa.
*
* @param contactID1 the ID of the first contact
* @param contactID2 the ID of the second contact
* @since Envoy Server Standalone v0.1-alpha
*/
public void addContactBidirectional(long contactID1, long contactID2) {
addContactBidirectional(getContactByID(contactID1), getContactByID(contactID2));
}
/**
* Adds a contact to the contact list of another contact and vice versa.
*
* @param contact1 the first contact
* @param contact2 the second contact
* @since Envoy Server v0.3-beta
*/
public void addContactBidirectional(Contact contact1, Contact contact2) {
2020-10-10 22:25:39 +02:00
// Add users to each others contact list
contact1.getContacts().add(contact2);
contact2.getContacts().add(contact1);
// Synchronize changes with the database
transaction(() -> { entityManager.merge(contact1); entityManager.merge(contact2); });
}
2020-10-10 22:25:39 +02:00
/**
* Removes a contact from the contact list of another contact and vice versa.
*
* @param contactID1 the ID of the first contact
* @param contactID2 the ID of the second contact
* @since Envoy Server v0.3-beta
*/
public void removeContactBidirectional(long contactID1, long contactID2) {
removeContactBidirectional(getContactByID(contactID1), getContactByID(contactID2));
}
2020-10-10 22:25:39 +02:00
/**
* Removes a contact from the contact list of another contact and vice versa.
*
* @param contact1 the first contact
* @param contact2 the second contact
* @since Envoy Server v0.3-beta
*/
public void removeContactBidirectional(Contact contact1, Contact contact2) {
2020-10-10 22:25:39 +02:00
// Remove users from each others contact list
contact1.getContacts().remove(contact2);
contact2.getContacts().remove(contact1);
2020-10-10 22:25:39 +02:00
// Synchronize changes with the database
transaction(() -> { entityManager.merge(contact1); entityManager.merge(contact2); });
2020-10-10 22:25:39 +02:00
}
/**
* @param user the User whose contacts should be retrieved
* @return the contacts of this User
* @since Envoy Server Standalone v0.1-alpha
*/
public List<User> getContacts(User user) {
return entityManager.createNamedQuery(User.findContacts).setParameter("user", user).getResultList();
}
private void persist(Object obj) { transaction(() -> entityManager.persist(obj)); }
private void merge(Object obj) { transaction(() -> entityManager.merge(obj)); }
private void remove(Object obj) { transaction(() -> entityManager.remove(obj)); }
/**
* Performs a transaction with the given Runnable, that should somewhere call
* {@link EntityManager}.
*
* @param entityManagerRelatedAction the action that changes something in the
* database
* @since Envoy Server v0.3-beta
*/
private void transaction(Runnable entityManagerRelatedAction) {
try {
transaction.begin();
entityManagerRelatedAction.run();
transaction.commit();
// Last transaction threw an error resulting in the transaction not being closed
} catch (final IllegalStateException e) {
if (transaction.isActive()) {
transaction.rollback();
transaction.begin();
entityManagerRelatedAction.run();
transaction.commit();
}
} catch (final RollbackException e2) {
// Apparently a major problem exists. Discard faulty transaction and then go on.
if (transaction.isActive()) {
transaction.rollback();
EnvoyLog.getLogger(PersistenceManager.class)
.log(Level.SEVERE, "Could not perform transaction, hence discarding it. It's likely that a serious issue exists.");
} else throw e2;
}
}
}