preparing for sending documents

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
2025-07-17 20:15:28 +02:00
parent 93449e4bad
commit 6a58087ace
19 changed files with 146 additions and 110 deletions

View File

@@ -16,10 +16,7 @@ public class Constants {
public static final String FIELD_HOST = "host";
public static final String FIELD_PORT = "port";
public static final String HOST = "mail.smtp.host";
public static final String JSONARRAY = "json array";
public static final String JSONOBJECT = "json object";
public static final String PORT = "mail.smtp.port";
public static final String RECEIVERS = "receivers";
public static final String SSL = "mail.smtp.ssl.enable";
public static final String SUBMISSION = "submission";

View File

@@ -9,12 +9,13 @@ import static de.srsoftware.umbrella.message.Constants.*;
import static java.lang.System.Logger.Level.*;
import de.srsoftware.configuration.Configuration;
import de.srsoftware.umbrella.core.api.PostBox;
import de.srsoftware.umbrella.core.api.Translator;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.Envelope;
import de.srsoftware.umbrella.core.model.UmbrellaUser;
import de.srsoftware.umbrella.core.model.User;
import de.srsoftware.umbrella.message.model.CombinedMessage;
import de.srsoftware.umbrella.message.model.Envelope;
import de.srsoftware.umbrella.message.model.PostBox;
import jakarta.activation.DataHandler;
import jakarta.mail.Message;
import jakarta.mail.MessagingException;
@@ -31,7 +32,7 @@ import java.util.function.BiFunction;
public class MessageSystem implements PostBox {
public static final System.Logger LOG = System.getLogger(MessageSystem.class.getSimpleName());
private final Timer timer = new Timer();
private record Receiver(UmbrellaUser user, de.srsoftware.umbrella.message.model.Message message){}
private record Receiver(User user, de.srsoftware.umbrella.core.model.Message message){}
private class SubmissionTask extends TimerTask{
@@ -99,11 +100,11 @@ public class MessageSystem implements PostBox {
private synchronized void processMessages(Integer scheduledHour) {
LOG.log(INFO,"Running {0}…",scheduledHour == null ? "instantly" : "scheduled at "+scheduledHour);
var queue = new ArrayList<>(this.queue);
var dueRecipients = new ArrayList<UmbrellaUser>();
List<UmbrellaUser> recipients = queue.stream().map(Envelope::receivers).flatMap(Set::stream).filter(Objects::nonNull).distinct().toList();
var dueRecipients = new ArrayList<User>();
List<User> recipients = queue.stream().map(Envelope::receivers).flatMap(Set::stream).filter(Objects::nonNull).distinct().toList();
{ // for known users: get notification preferences, fallback to _immediately_ for unknown users
for (UmbrellaUser recv : recipients) {
for (User recv : recipients) {
if (recv instanceof UmbrellaUser uu) {
try {
if (!db.getSettings(uu).sendAt(scheduledHour)) continue;
@@ -157,7 +158,7 @@ public class MessageSystem implements PostBox {
}
private void send(CombinedMessage message, UmbrellaUser receiver, Date date) throws MessagingException {
private void send(CombinedMessage message, User receiver, Date date) throws MessagingException {
LOG.log(TRACE,"Sending combined message to {0}…",receiver);
session = session();
MimeMessage msg = new MimeMessage(session);

View File

@@ -1,39 +0,0 @@
/* © SRSoftware 2025 */
package de.srsoftware.umbrella.message.model;
import static de.srsoftware.umbrella.core.Constants.*;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.invalidFieldException;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import java.util.Arrays;
import java.util.Base64;
import java.util.Objects;
import java.util.Set;
import org.json.JSONObject;
public record Attachment(String name, String mime, byte[] content) {
private static final Base64.Decoder BASE64 = Base64.getDecoder();
public static <T> Attachment of(JSONObject json) throws UmbrellaException {
for (var key : Set.of(NAME, MIME, DATA)) {
if (!json.has(key)) throw missingFieldException(key);
}
if (!(json.get(NAME) instanceof String name)) throw invalidFieldException(NAME,STRING);
if (!(json.get(MIME) instanceof String mime)) throw invalidFieldException(MIME,STRING);
if (!(json.get(DATA) instanceof String data)) throw invalidFieldException(DATA,STRING);
return new Attachment(name,mime, BASE64.decode(data));
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Attachment that)) return false;
return Objects.equals(name, that.name) && Objects.equals(mime, that.mime) && Objects.deepEquals(content, that.content);
}
@Override
public int hashCode() {
return Objects.hash(name, mime, Arrays.hashCode(content));
}
}

View File

@@ -5,6 +5,8 @@ import static java.lang.System.Logger.Level.DEBUG;
import static java.lang.System.Logger.Level.TRACE;
import static java.text.MessageFormat.format;
import de.srsoftware.umbrella.core.model.Attachment;
import de.srsoftware.umbrella.core.model.Message;
import de.srsoftware.umbrella.core.model.UmbrellaUser;
import java.util.*;
import java.util.function.BiFunction;

View File

@@ -1,68 +0,0 @@
/* © SRSoftware 2025 */
package de.srsoftware.umbrella.message.model;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.invalidFieldException;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException;
import static de.srsoftware.umbrella.message.Constants.*;
import static java.text.MessageFormat.format;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.EmailAddress;
import de.srsoftware.umbrella.core.model.UmbrellaUser;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.json.JSONArray;
import org.json.JSONObject;
public class Envelope {
private Message message;
private Set<UmbrellaUser> receivers;
public Envelope(Message message, UmbrellaUser receiver){
this(message,new HashSet<>(Set.of(receiver)));
}
public Envelope(Message message, HashSet<UmbrellaUser> receivers) {
this.message = message;
this.receivers = receivers;
}
/**
* TODO: this is legacy, move to legacy module!
* @param json
* @return
* @throws UmbrellaException
*/
public static Envelope from(JSONObject json) throws UmbrellaException {
if (!json.has(RECEIVERS)) throw missingFieldException(RECEIVERS);
var message = Message.from(json);
var obj = json.get(RECEIVERS);
if (obj instanceof JSONObject) obj = new JSONArray(List.of(obj));
if (!(obj instanceof JSONArray receiverList)) throw invalidFieldException(RECEIVERS,JSONARRAY);
var receivers = new HashSet<UmbrellaUser>();
for (var o : receiverList){
if (!(o instanceof JSONObject receiverData)) throw invalidFieldException("entries of "+RECEIVERS,JSONOBJECT);
receivers.add(UmbrellaUser.of(receiverData));
}
return new Envelope(message,receivers);
}
public boolean isFor(UmbrellaUser receiver) {
return receivers.contains(receiver);
}
public Message message(){
return message;
}
public Set<UmbrellaUser> receivers(){
return receivers;
}
@Override
public String toString() {
return format("{0} (to: {1}), subject: {2}",getClass().getSimpleName(),receivers.stream().map(UmbrellaUser::email).map(EmailAddress::toString).collect(Collectors.joining(", ")),message.subject());
}
}

View File

@@ -1,58 +0,0 @@
/* © SRSoftware 2025 */
package de.srsoftware.umbrella.message.model;
import static de.srsoftware.tools.Optionals.isSet;
import static de.srsoftware.umbrella.core.Constants.*;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.invalidFieldException;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException;
import static de.srsoftware.umbrella.message.Constants.*;
import static java.text.MessageFormat.format;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.UmbrellaUser;
import java.util.*;
import org.json.JSONArray;
import org.json.JSONObject;
public record Message(UmbrellaUser sender, String subject, String body, Map<String,String> fills, List<Attachment> attachments) {
public static Message from(JSONObject json) throws UmbrellaException {
for (var key : Set.of(SENDER, SUBJECT, BODY)) {
if (!json.has(key)) throw missingFieldException(key);
}
if (!(json.get(SENDER) instanceof JSONObject senderObject)) throw invalidFieldException(SENDER,JSONOBJECT);
if (!(json.get(SUBJECT) instanceof String subject && isSet(subject))) throw invalidFieldException(SUBJECT,STRING);
if (!(json.get(BODY) instanceof String body && isSet(body))) throw invalidFieldException(BODY,STRING);
var user = UmbrellaUser.of(senderObject);
if (!(user instanceof UmbrellaUser sender)) throw new UmbrellaException(400,"Sender is not an umbrella user!");
var attachments = new ArrayList<Attachment>();
if (json.has(ATTACHMENTS)){
var jsonAttachments = json.get(ATTACHMENTS);
if (jsonAttachments instanceof JSONObject obj) jsonAttachments = new JSONArray(List.of(obj));
if (jsonAttachments instanceof JSONArray arr){
for (var att : arr){
if (!(att instanceof JSONObject o)) throw new UmbrellaException(400,"Attachments contains entry that is not an object: {}",att);
var attachment = Attachment.of(o);
attachments.add(attachment);
}
}
}
return new Message(sender,subject,body,null, attachments);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Message message)) return false;
return Objects.equals(sender, message.sender) && Objects.equals(subject, message.subject) && Objects.equals(body, message.body) && Objects.equals(attachments, message.attachments);
}
@Override
public int hashCode() {
return Objects.hash(subject, body, attachments);
}
@Override
public String toString() {
return format("{0}(from: {1}), subject: {2}",getClass().getSimpleName(),sender,subject);
}
}

View File

@@ -1,6 +0,0 @@
/* © SRSoftware 2025 */
package de.srsoftware.umbrella.message.model;
public interface PostBox {
public void send(Envelope envelope);
}