Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
arc12012 committed Apr 26, 2017
1 parent fb8c77f commit faf025c
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 20 deletions.
Expand Up @@ -370,7 +370,7 @@ function fuzzyFilter(deviceArray)
}
////// Second pass: filter by MAC address
options.extract = function(arg) {return arg.mac;};
options.pre = "<span style='color: orange; font-size: 115%'><b>"
options.pre = "<span style='color: darkorange; font-size: 115%'><b>"
var macFilterResults = fuzzy.filter(searchText, deviceArray, options);
// replace releveant field with bolded string
for (var i = macFilterResults.length - 1; i >= 0; i--) {
Expand Down Expand Up @@ -402,7 +402,7 @@ function fuzzyFilter(deviceArray)
}
////// Sixth pass: filter by Serial
options.extract = function(arg) {return arg.serial;};
options.pre = "<span style='color: violet; font-size: 115%'><b>"
options.pre = "<span style='color: deepviolet; font-size: 115%'><b>"
var serialFilterResults = fuzzy.filter(searchText, deviceArray, options);
// replace releveant field with bolded string
for (var i = serialFilterResults.length - 1; i >= 0; i--) {
Expand Down
10 changes: 5 additions & 5 deletions 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"%>

Expand Down Expand Up @@ -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");
</script>
Expand Down
54 changes: 54 additions & 0 deletions src/database/TicketQueries.java
Expand Up @@ -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;
}
}

44 changes: 31 additions & 13 deletions src/utilities/AdminNotificationQueue.java
@@ -1,5 +1,6 @@
package utilities;

import java.sql.SQLException;
import java.util.Set;

import entities.Ticket;
Expand All @@ -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<Thread> 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<threadArray.length; i++) {
if(threadArray[i].getName().equals(threadName)) {
threads+=threadArray[i].getName()+"\n";
match=true;
}
}
System.out.println(match ? " Match!" : " No match");
System.out.println("Active threads-n"+threads+ (match ? " Found Match" : " No match"));
if(match){
System.out.println("Redundant Admin Notification Queue thread ("+threadName+") will terminate");
return; //should kill thread
Expand All @@ -47,22 +56,31 @@ public class AdminNotificationQueue extends Thread {
// Finally do the thing we came for
switch(instructions){
case "adminUrgentRequest":
new Mail(admin).sendUrgentRequest(tickets);
break;
for (User ad : admins) {
new Mail(ad).sendUrgentRequest(tickets);
}
break;
case "adminDeviceRequest":
queueRequestNotification();
break;
queueRequestNotification();
break;
default:
System.out.println("Admin notification queue could not recognize instructions");
System.out.println("Admin notification queue could not recognize instructions");
}
}
} catch(Exception e) {
e.printStackTrace();
} finally {

}
queueRequestNotification() {


}
private void queueRequestNotification() throws ClassNotFoundException, InterruptedException, SQLException {
threadWaitTime = 10*60000;//24*60*60*1000//frequency==1 ? 6*60*60*1000 : 24*60*60*1000;
Thread.sleep(threadWaitTime);
for(User a : admins){
int notificationPreferences = a.getNotificationPreferences();
boolean enabled = (notificationPreferences >> 3)==1;
if(enabled) new Mail(a).sendAdminSummary(threadWaitTime);
}
}
}
}
73 changes: 73 additions & 0 deletions src/utilities/Mail.java
Expand Up @@ -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;

Expand Down Expand Up @@ -262,6 +264,77 @@ public class Mail {
}
}

public void sendAdminSummary(int threadWaitTime) throws ClassNotFoundException, InterruptedException, SQLException {
String subject = "Summary of recent activity";
String message = "<!DOCTYPE html>"
+ "<html>"
+ "<body>"
+ "<h4>Here's what's hapened since you've been away</h4>"
+ "<p>{TICKET COUNT} device{req-s} {req-have/has} been requested.</p>"
+ "<div name = 'tickets' style='text-indent: 20px'>"
+ "{TICKETS GO HERE}"
+ "</div>"
+ "<p>{RETURN COUNT} device{ret-s} {ret-have/has} been marked returned and should be on {ret-their/its} way back.</p>"
+ "<div name = 'returns' style='text-indent: 20px'"
+ "{RETURNS GO HERE}"
+ "</div>"
+ "</body>"
+ "<footer style='text-align: center;'><font size='1'>To change notification settings, please visit your <a href='"+profileLink+"'>profile settings page</a></font></footer>"
+ "</html>";
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+="<p>Ticket #"+tic.getId()+" - "+tic.getDeviceName()+" - requested by "+tic.getRequestorName()+"</p>";
}
String returnString = "";
for (Ticket tic : returns) {
returnString+="<p>Ticket #"+tic.getId()+" - "+tic.getDeviceName()+" - returned by "+tic.getRequestorName()+"</p>";
}
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 {
Expand Down

0 comments on commit faf025c

Please sign in to comment.