From faf025cfc274b3475217cec429c0a243010b4302 Mon Sep 17 00:00:00 2001 From: Adam R Claxton Date: Tue, 25 Apr 2017 22:30:07 -0400 Subject: [PATCH] There is only going to be 1 admin notification thread :( Turns out threads aren't scaleable. Starting up more than one at a time costs a lot of overhead. Well not when we do it on any other page, but they make the orderFormHandler page take forever to redirect. Like, long enough that the connection cleanup thread freaks out and crashes everything. There is a connection cleanup thread btw. Why? What connection is even open there? Who knows. Designating a single thread to do nothing but start notification queue threads for everyone doesn't help. Using a thread pool event scheduler doesn't help. Using a thread pool event scheduler to schedule a single thread which is designated to do nothing but start notification queue threads for everyone? Nope, redirict page waits that full minute and then wait for every thread to begin, then crashes. I give up. Every admin is now on the same mailing list. --- .../administration/adminDeviceSettings.jsp | 4 +- .../webpages/redirect/orderFormHandler.jsp | 10 +-- src/database/TicketQueries.java | 54 ++++++++++++++ src/utilities/AdminNotificationQueue.java | 44 +++++++---- src/utilities/Mail.java | 73 +++++++++++++++++++ 5 files changed, 165 insertions(+), 20 deletions(-) diff --git a/WebContent/html/webpages/administration/adminDeviceSettings.jsp b/WebContent/html/webpages/administration/adminDeviceSettings.jsp index 24dc22b..10920df 100644 --- a/WebContent/html/webpages/administration/adminDeviceSettings.jsp +++ b/WebContent/html/webpages/administration/adminDeviceSettings.jsp @@ -370,7 +370,7 @@ function fuzzyFilter(deviceArray) } ////// Second pass: filter by MAC address options.extract = function(arg) {return arg.mac;}; - options.pre = "" + options.pre = "" var macFilterResults = fuzzy.filter(searchText, deviceArray, options); // replace releveant field with bolded string for (var i = macFilterResults.length - 1; i >= 0; i--) { @@ -402,7 +402,7 @@ function fuzzyFilter(deviceArray) } ////// Sixth pass: filter by Serial options.extract = function(arg) {return arg.serial;}; - options.pre = "" + options.pre = "" var serialFilterResults = fuzzy.filter(searchText, deviceArray, options); // replace releveant field with bolded string for (var i = serialFilterResults.length - 1; i >= 0; i--) { diff --git a/WebContent/html/webpages/redirect/orderFormHandler.jsp b/WebContent/html/webpages/redirect/orderFormHandler.jsp index afca845..5587486 100644 --- a/WebContent/html/webpages/redirect/orderFormHandler.jsp +++ b/WebContent/html/webpages/redirect/orderFormHandler.jsp @@ -1,4 +1,4 @@ -<%@ page import = "database.*,entities.*,utilities.*" %> +<%@ page import = "database.*,entities.*,utilities.*,java.util.concurrent.*" %> <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> @@ -111,10 +111,10 @@ User[] admins = EmployeeQueries.getAllAdmins(); // route these emails through a notification queue. Therefore the only difference here between an // urgent order and a regular order will be the instructions we pass to the queue. String instructions = (urgent) ? "adminUrgentRequest" : "adminDeviceRequest"; -for (int i = admins.length - 1; i >= 0; i--) { - AdminNotificationQueue Q = new AdminNotificationQueue(admins[i],instructions,tickets); - Q.start(); -} + +AdminNotificationQueue Q = new AdminNotificationQueue(admins,instructions,tickets); +Q.start(); + %> window.location.replace("../shoppingCart.jsp"); diff --git a/src/database/TicketQueries.java b/src/database/TicketQueries.java index 389b0ad..070b45c 100644 --- a/src/database/TicketQueries.java +++ b/src/database/TicketQueries.java @@ -407,5 +407,59 @@ public class TicketQueries { connection.close(); return answer; } + + public static Object[] getRecentClientActivity() throws InterruptedException, ClassNotFoundException, SQLException { + // This method will be called by several threads simultaneously so I'm trying to stagger them a bit to reduce db load + Thread.sleep((int)Math.random()*15000); + System.getenv("VCAP_SERVICES"); + Class.forName("com.mysql.jdbc.Driver"); + Connection connect = DriverManager.getConnection(database, user, password); + Statement stmt = connect.createStatement(); + String query ="SELECT ticket.*, employee.Name AS 'username', devices.Device_Name, devices.Status AS 'deviceStatus', location.Name AS 'locationname' " + + "FROM ticket INNER JOIN employee ON ticket.Requestor = employee.Employee_ID " + + "INNER JOIN devices ON ticket.Device_ID = devices.Device_ID " + + "INNER JOIN location ON ticket.Location = location.Location_ID " + + "WHERE ticket.Status='Requested' OR devices.Status='Returning'"; + System.out.println("Executing query: "+query); + ResultSet results = stmt.executeQuery(query); + results.last(); + Ticket[] allTicketAcivity = new Ticket[results.getRow()]; + results.beforeFirst(); + while(results.next()) + { + allTicketAcivity[results.getRow()-1] = new Ticket( + results.getInt("Ticket_ID"), + results.getInt("Requestor"), + results.getLong("Request_Date"), + results.getInt("Location"), + results.getInt("Device_ID"), + results.getString("Status"), + results.getLong("Status_Date_Fields"), + results.getString("Return_Date"), + results.getString("username"), + results.getString("Device_Name"), + results.getString("locationname"), + results.getInt("Permanent_Order") + ); + } + stmt.close(); + connect.close(); + int requestCount = 0; + for(Ticket tic : allTicketAcivity) { + if(tic.getStatus().equals("Requested")) + requestCount++; + } + Ticket[] requests = new Ticket[requestCount]; + Ticket[] returns = new Ticket[allTicketAcivity.length-requestCount]; + requestCount=0; + int returnCount=0; + for(Ticket t : allTicketAcivity) + { + if(t.getStatus().equals("Requested")) requests[requestCount++] = t; + else returns[returnCount++] = t; + } + Object[] finalArray = {requests,returns}; + return finalArray; + } } diff --git a/src/utilities/AdminNotificationQueue.java b/src/utilities/AdminNotificationQueue.java index b53f26a..5e85276 100644 --- a/src/utilities/AdminNotificationQueue.java +++ b/src/utilities/AdminNotificationQueue.java @@ -1,5 +1,6 @@ package utilities; +import java.sql.SQLException; import java.util.Set; import entities.Ticket; @@ -13,31 +14,39 @@ public class AdminNotificationQueue extends Thread { */ private String instructions=""; private int threadWaitTime; - private User admin; + private User[] admins; private Ticket[] tickets; // The tickets parameter will most likely be relevant only when using this class to // dispatch emails immediately with threads - public AdminNotificationQueue(User admin, String initialization, Ticket[] tickets) + public AdminNotificationQueue(User[] admins, String initialization, Ticket[] tickets) { instructions=initialization; - this.admin=admin; + this.admins=admins; this.tickets=tickets; } public void run() { try { - String threadName = instructions+admin.getID(); + Thread.sleep(1); + } catch (InterruptedException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + try { + String threadName = instructions; Set threadSet = Thread.getAllStackTraces().keySet(); Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]); boolean match = false; System.out.println("Admin Notification Queue: Chacking active threads for match with "+threadName+"..."); + String threads = ""; for (int i=0; i> 3)==1; + if(enabled) new Mail(a).sendAdminSummary(threadWaitTime); } } -} +} \ No newline at end of file diff --git a/src/utilities/Mail.java b/src/utilities/Mail.java index 1d5c429..9cab145 100644 --- a/src/utilities/Mail.java +++ b/src/utilities/Mail.java @@ -2,10 +2,12 @@ package utilities; import java.awt.HeadlessException; import java.io.IOException; +import java.sql.SQLException; import java.util.*; import javax.mail.*; import javax.mail.internet.*; +import database.TicketQueries; import entities.Ticket; import entities.User; @@ -262,6 +264,77 @@ public class Mail { } } + public void sendAdminSummary(int threadWaitTime) throws ClassNotFoundException, InterruptedException, SQLException { + String subject = "Summary of recent activity"; + String message = "" + + "" + + "" + + "

Here's what's hapened since you've been away

" + + "

{TICKET COUNT} device{req-s} {req-have/has} been requested.

" + + "
" + + "{TICKETS GO HERE}" + + "
" + + "

{RETURN COUNT} device{ret-s} {ret-have/has} been marked returned and should be on {ret-their/its} way back.

" + + "
" + + "" + + "" + + ""; + Object[] ticketActivity = TicketQueries.getRecentClientActivity(); + Ticket[] requests = (Ticket[]) ticketActivity[0]; + String array = "Requests array:\n"; + for (Ticket t : requests) { + array+=" "+t.getDeviceName()+"\n"; + } + // System.out.println(array); + Ticket[] returns = (Ticket[]) ticketActivity[1]; + array = "Returns array:\n"; + for (Ticket t :returns) { + array+=" "+t.getDeviceName()+"\n"; + } + // System.out.println(array); + String requestString = ""; + for (Ticket tic : requests) { + requestString+="

Ticket #"+tic.getId()+" - "+tic.getDeviceName()+" - requested by "+tic.getRequestorName()+"

"; + } + String returnString = ""; + for (Ticket tic : returns) { + returnString+="

Ticket #"+tic.getId()+" - "+tic.getDeviceName()+" - returned by "+tic.getRequestorName()+"

"; + } + message=message.replace("{req-s}",(requests.length==1 ? "" : "s")); //make occurances of 'device' and 'ticket' plural or singular + message=message.replace("{ret-s}",(returns.length==1 ? "" : "s")); + message=message.replace("{req-have/has}",(requests.length==1 ? "has" : "have")); + message=message.replace("{ret-have/has}",(returns.length==1 ? "has" : "have")); + message=message.replace("{ret-their/its}",(returns.length==1 ? "its" : "their")); + message=message.replace("{TICKET COUNT}",""+requests.length); + message=message.replace("{RETURN COUNT}",""+returns.length); + message=message.replace("{TICKETS GO HERE}",requestString); + message=message.replace("{RETURNS GO HERE}",returnString); + Properties properties = System.getProperties(); + properties = setProp(sender, client.getEmail()); + Session session = Session.getInstance(properties, new Authenticator() { + protected PasswordAuthentication getPasswordAuthentication(){ + return new PasswordAuthentication(sender, password); + } + }); + + try { + Address address = new InternetAddress(client.getEmail()); + MimeMessage mess = new MimeMessage(session); + mess.setFrom(address); + mess.addRecipient(Message.RecipientType.TO, address); + mess.setSubject(subject); + mess.setText(message,"utf-8","html"); + mess.saveChanges(); + Transport.send(mess); + System.out.println("Sent message successfully...."); + + } catch (Exception mex) { + mex.printStackTrace(); + } + } + private Properties setProp(String email, String targetEmail) { Properties props = null; try {