001package edu.pdx.cs410J.grader.poa; 002 003import com.google.common.annotations.VisibleForTesting; 004import com.google.common.eventbus.EventBus; 005import com.google.common.eventbus.Subscribe; 006import com.google.common.io.CharStreams; 007import com.google.inject.Inject; 008import com.google.inject.Singleton; 009import com.google.inject.name.Named; 010import edu.pdx.cs410J.grader.EmailAttachmentProcessor; 011import edu.pdx.cs410J.grader.GraderEmailAccount; 012import jakarta.mail.Message; 013import jakarta.mail.MessagingException; 014 015import java.io.IOException; 016import java.io.InputStream; 017import java.io.InputStreamReader; 018import java.io.StringWriter; 019import java.time.LocalDateTime; 020import java.time.ZoneId; 021import java.util.Arrays; 022import java.util.Date; 023import java.util.concurrent.Executor; 024import java.util.concurrent.Future; 025import java.util.concurrent.FutureTask; 026 027@Singleton 028public class POASubmissionsDownloader { 029 030 static final String POA_FOLDER_NAME = "poa"; 031 private final EventBus bus; 032 private final Executor executor; 033 034 @Inject 035 public POASubmissionsDownloader(EventBus bus, @Named("POADownloaderExecutor") Executor executor) { 036 this.bus = bus; 037 this.executor = executor; 038 this.bus.register(this); 039 } 040 041 @Subscribe 042 public void downloadSubmissions(EmailCredentials credentials) throws MessagingException { 043 GraderEmailAccount account = new GraderEmailAccount(credentials.getEmailAddress(), credentials.getPassword(), this::fireStatusMessage); 044 downloadSubmissions(account); 045 } 046 047 @VisibleForTesting 048 Future<?> downloadSubmissions(String emailServerHostName, int emailServerHostPort, EmailCredentials credentials) { 049 GraderEmailAccount account = new GraderEmailAccount(emailServerHostName, emailServerHostPort, credentials.getEmailAddress(), credentials.getPassword(), true, this::fireStatusMessage); 050 return downloadSubmissions(account); 051 } 052 053 private Future<?> downloadSubmissions(GraderEmailAccount account) { 054 FutureTask<Void> future = new FutureTask<>(() -> account.fetchAttachmentsFromUnreadMessagesInFolder(POA_FOLDER_NAME, new POAAttachmentProcessor()), null); 055 this.executor.execute(future); 056 return future; 057 } 058 059 private void fireStatusMessage(String statusMessage) { 060 this.bus.post(new StatusMessage(statusMessage)); 061 } 062 063 private void extractPOASubmissionFromAttachment(Message message, InputStream inputStream, String contentType) { 064 try { 065 String submitter = getSender(message); 066 LocalDateTime submitTime = getTimeMessageWasSent(message); 067 String content = extractPOAContentFrom(inputStream); 068 POASubmission submission = POASubmission.builder() 069 .setSubject(message.getSubject()) 070 .setSubmitter(submitter) 071 .setSubmitTime(submitTime) 072 .setContent(content) 073 .setContentType(contentType) 074 .create(); 075 076 this.bus.post(submission); 077 078 } catch (IOException | MessagingException ex) { 079 throw new IllegalStateException("While working with message", ex); 080 } 081 082 } 083 084 private String extractPOAContentFrom(InputStream inputStream) throws IOException { 085 StringWriter sw = new StringWriter(); 086 CharStreams.copy(new InputStreamReader(inputStream), sw); 087 return sw.toString(); 088 } 089 090 private LocalDateTime getTimeMessageWasSent(Message message) throws MessagingException { 091 Date date = message.getSentDate(); 092 return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); 093 } 094 095 private String getSender(Message message) throws MessagingException { 096 return message.getFrom()[0].toString(); 097 } 098 099 private class POAAttachmentProcessor implements EmailAttachmentProcessor { 100 @Override 101 public void processAttachment(Message message, String fileName, InputStream inputStream, String contentType) { 102 extractPOASubmissionFromAttachment(message, inputStream, contentType); 103 } 104 105 @Override 106 public Iterable<? extends String> getSupportedContentTypes() { 107 return Arrays.asList("text/plain", "text/html"); 108 } 109 } 110}