Multithreading announcements.

This commit is contained in:
NovaFox161
2018-06-16 18:23:56 -05:00
parent 398a831586
commit 58db0e1f7f
7 changed files with 374 additions and 44 deletions

View File

@@ -21,7 +21,7 @@ import java.util.UUID;
* Website: www.cloudcraftgaming.com
* For Project: DisCal-Discord-Bot
*/
@SuppressWarnings({"SqlResolve", "UnusedReturnValue", "SqlNoDataSourceInspection"})
@SuppressWarnings({"SqlResolve", "UnusedReturnValue", "SqlNoDataSourceInspection", "Duplicates"})
public class DatabaseManager {
private static DatabaseManager instance;
private DatabaseInfo databaseInfo;
@@ -173,7 +173,7 @@ public class DatabaseManager {
PreparedStatement statement = databaseInfo.getConnection().prepareStatement(query);
ResultSet res = statement.executeQuery();
Boolean hasStuff = res.next();
boolean hasStuff = res.next();
if (!hasStuff || res.getString("API_KEY") == null) {
//Data not present, add to DB.
@@ -228,7 +228,7 @@ public class DatabaseManager {
PreparedStatement statement = databaseInfo.getConnection().prepareStatement(query);
ResultSet res = statement.executeQuery();
Boolean hasStuff = res.next();
boolean hasStuff = res.next();
if (!hasStuff || res.getString("GUILD_ID") == null) {
//Data not present, add to DB.
@@ -309,7 +309,7 @@ public class DatabaseManager {
PreparedStatement statement = databaseInfo.getConnection().prepareStatement(query);
ResultSet res = statement.executeQuery();
Boolean hasStuff = res.next();
boolean hasStuff = res.next();
if (!hasStuff || res.getString("GUILD_ID") == null) {
//Data not present, add to DB.
@@ -358,7 +358,7 @@ public class DatabaseManager {
* @param announcement The announcement object to add to the database.
* @return <code>true</code> if successful, else <code>false</code>.
*/
public Boolean updateAnnouncement(Announcement announcement) {
public boolean updateAnnouncement(Announcement announcement) {
try {
if (databaseInfo.getMySQL().checkConnection()) {
String announcementTableName = String.format("%sannouncements", databaseInfo.getPrefix());
@@ -367,7 +367,7 @@ public class DatabaseManager {
PreparedStatement statement = databaseInfo.getConnection().prepareStatement(query);
ResultSet res = statement.executeQuery();
Boolean hasStuff = res.next();
boolean hasStuff = res.next();
if (!hasStuff || res.getString("ANNOUNCEMENT_ID") == null) {
//Data not present, add to db.
@@ -432,7 +432,7 @@ public class DatabaseManager {
return false;
}
public Boolean updateEventData(EventData data) {
public boolean updateEventData(EventData data) {
try {
if (databaseInfo.getMySQL().checkConnection()) {
String eventTableName = String.format("%sevents", databaseInfo.getPrefix());
@@ -445,7 +445,7 @@ public class DatabaseManager {
PreparedStatement statement = databaseInfo.getConnection().prepareStatement(query);
ResultSet res = statement.executeQuery();
Boolean hasStuff = res.next();
boolean hasStuff = res.next();
if (!hasStuff || res.getString("EVENT_ID") == null) {
//Data not present, add to DB.
@@ -485,7 +485,7 @@ public class DatabaseManager {
return false;
}
public Boolean updateRsvpData(RsvpData data) {
public boolean updateRsvpData(RsvpData data) {
try {
if (databaseInfo.getMySQL().checkConnection()) {
String rsvpTableName = String.format("%srsvp", databaseInfo.getPrefix());
@@ -494,7 +494,7 @@ public class DatabaseManager {
PreparedStatement statement = databaseInfo.getConnection().prepareStatement(query);
ResultSet res = statement.executeQuery();
Boolean hasStuff = res.next();
boolean hasStuff = res.next();
if (!hasStuff || res.getString("EVENT_ID") == null) {
//Data not present, add to DB.
@@ -553,7 +553,7 @@ public class DatabaseManager {
PreparedStatement statement = databaseInfo.getConnection().prepareStatement(query);
ResultSet res = statement.executeQuery();
Boolean hasStuff = res.next();
boolean hasStuff = res.next();
if (hasStuff && res.getString("API_KEY") != null) {
UserAPIAccount account = new UserAPIAccount();
@@ -588,7 +588,7 @@ public class DatabaseManager {
PreparedStatement statement = databaseInfo.getConnection().prepareStatement(query);
ResultSet res = statement.executeQuery();
Boolean hasStuff = res.next();
boolean hasStuff = res.next();
if (hasStuff && res.getString("GUILD_ID") != null) {
settings.setUseExternalCalendar(res.getBoolean("EXTERNAL_CALENDAR"));
@@ -760,7 +760,7 @@ public class DatabaseManager {
}
public Integer getCalendarCount() {
Integer amount = -1;
int amount = -1;
try {
if (databaseInfo.getMySQL().checkConnection()) {
String calendarTableName = String.format("%scalendars", databaseInfo.getPrefix());
@@ -918,7 +918,7 @@ public class DatabaseManager {
PreparedStatement statement = databaseInfo.getConnection().prepareStatement(query);
ResultSet res = statement.executeQuery();
Boolean hasStuff = res.next();
boolean hasStuff = res.next();
if (hasStuff && res.getString("ANNOUNCEMENT_ID") != null) {
Announcement announcement = new Announcement(announcementId, guildId);
@@ -1026,6 +1026,44 @@ public class DatabaseManager {
return announcements;
}
public ArrayList<Announcement> getAnnouncements(AnnouncementType type) {
ArrayList<Announcement> announcements = new ArrayList<>();
try {
if (databaseInfo.getMySQL().checkConnection()) {
String announcementTableName = String.format("%sannouncements", databaseInfo.getPrefix());
PreparedStatement stmt = databaseInfo.getConnection().prepareStatement("SELECT * FROM " + announcementTableName + " WHERE ANNOUNCEMENT_TYPE = ?");
stmt.setString(1, type.name());
ResultSet res = stmt.executeQuery();
while (res.next()) {
if (res.getString("ANNOUNCEMENT_ID") != null) {
Announcement announcement = new Announcement(UUID.fromString(res.getString("ANNOUNCEMENT_ID")), Long.valueOf(res.getString("GUILD_ID")));
announcement.setSubscriberRoleIdsFromString(res.getString("SUBSCRIBERS_ROLE"));
announcement.setSubscriberUserIdsFromString(res.getString("SUBSCRIBERS_USER"));
announcement.setAnnouncementChannelId(res.getString("CHANNEL_ID"));
announcement.setAnnouncementType(type);
announcement.setEventId(res.getString("EVENT_ID"));
announcement.setEventColor(EventColor.fromNameOrHexOrID(res.getString("EVENT_COLOR")));
announcement.setHoursBefore(res.getInt("HOURS_BEFORE"));
announcement.setMinutesBefore(res.getInt("MINUTES_BEFORE"));
announcement.setInfo(res.getString("INFO"));
announcement.setEnabled(res.getBoolean("ENABLED"));
announcement.setInfoOnly(res.getBoolean("INFO_ONLY"));
announcements.add(announcement);
}
}
stmt.close();
}
} catch (SQLException e) {
Logger.getLogger().exception(null, "Failed to get all announcements by type.", e, this.getClass(), true);
}
return announcements;
}
public ArrayList<Announcement> getEnabledAnnouncements() {
ArrayList<Announcement> announcements = new ArrayList<>();
try {
@@ -1059,13 +1097,53 @@ public class DatabaseManager {
stmt.close();
}
} catch (SQLException e) {
Logger.getLogger().exception(null, "Failed to get all announcements.", e, this.getClass(), true);
Logger.getLogger().exception(null, "Failed to get all enabled announcements.", e, this.getClass(), true);
}
return announcements;
}
public Integer getAnnouncementCount() {
public ArrayList<Announcement> getEnabledAnnouncements(AnnouncementType type) {
ArrayList<Announcement> announcements = new ArrayList<>();
try {
if (databaseInfo.getMySQL().checkConnection()) {
String announcementTableName = String.format("%sannouncements", databaseInfo.getPrefix());
PreparedStatement stmt = databaseInfo.getConnection().prepareStatement("SELECT * FROM " + announcementTableName + " WHERE ENABLED = 1 AND ANNOUNCEMENT_ID = ?");
stmt.setString(1, type.name());
ResultSet res = stmt.executeQuery();
while (res.next()) {
if (res.getString("ANNOUNCEMENT_ID") != null) {
Announcement announcement = new Announcement(UUID.fromString(res.getString("ANNOUNCEMENT_ID")), Long.valueOf(res.getString("GUILD_ID")));
announcement.setSubscriberRoleIdsFromString(res.getString("SUBSCRIBERS_ROLE"));
announcement.setSubscriberUserIdsFromString(res.getString("SUBSCRIBERS_USER"));
announcement.setAnnouncementChannelId(res.getString("CHANNEL_ID"));
announcement.setAnnouncementType(type);
announcement.setEventId(res.getString("EVENT_ID"));
announcement.setEventColor(EventColor.fromNameOrHexOrID(res.getString("EVENT_COLOR")));
announcement.setHoursBefore(res.getInt("HOURS_BEFORE"));
announcement.setMinutesBefore(res.getInt("MINUTES_BEFORE"));
announcement.setInfo(res.getString("INFO"));
announcement.setInfoOnly(res.getBoolean("INFO_ONLY"));
//The announcement is obviously enabled if we have gotten here lol
announcement.setEnabled(true);
announcements.add(announcement);
}
}
stmt.close();
}
} catch (SQLException e) {
Logger.getLogger().exception(null, "Failed to get all enabled announcements by type.", e, this.getClass(), true);
}
return announcements;
}
public int getAnnouncementCount() {
int amount = -1;
try {
if (databaseInfo.getMySQL().checkConnection()) {
@@ -1096,7 +1174,7 @@ public class DatabaseManager {
* @param announcementId The ID of the announcement to delete.
* @return <code>true</code> if successful, else <code>false</code>.
*/
public Boolean deleteAnnouncement(String announcementId) {
public boolean deleteAnnouncement(String announcementId) {
try {
if (databaseInfo.getMySQL().checkConnection()) {
String announcementTableName = String.format("%sannouncements", databaseInfo.getPrefix());
@@ -1115,7 +1193,7 @@ public class DatabaseManager {
return false;
}
public Boolean deleteAnnouncementsForEvent(long guildId, String eventId) {
public boolean deleteAnnouncementsForEvent(long guildId, String eventId) {
try {
if (databaseInfo.getMySQL().checkConnection()) {
String announcementTableName = String.format("%sannouncements", databaseInfo.getPrefix());
@@ -1136,7 +1214,7 @@ public class DatabaseManager {
return false;
}
public Boolean deleteEventData(String eventId) {
public boolean deleteEventData(String eventId) {
try {
if (databaseInfo.getMySQL().checkConnection()) {
String eventTable = String.format("%sevents", databaseInfo.getPrefix());

View File

@@ -4,6 +4,7 @@ import com.cloudcraftgaming.discal.api.DisCalAPI;
import com.cloudcraftgaming.discal.api.database.DatabaseManager;
import com.cloudcraftgaming.discal.bot.internal.network.discordbots.UpdateDisBotData;
import com.cloudcraftgaming.discal.bot.internal.network.discordpw.UpdateDisPwData;
import com.cloudcraftgaming.discal.bot.module.announcement.AnnouncementThreader;
import com.cloudcraftgaming.discal.logger.Logger;
import com.cloudcraftgaming.discal.web.handler.DiscordAccountHandler;
import sx.blah.discord.util.DiscordException;
@@ -82,16 +83,8 @@ public class ApplicationHandler {
//MY CODE: Gracefully exit processes:
System.out.println("Restarting Discord bot!");
try {
DisCalAPI.getAPI().getClient().logout();
} catch (DiscordException e) {
//No need to print, exiting anyway.
}
TimeManager.getManager().shutdown();
DatabaseManager.getManager().disconnectFromMySQL();
// exit
System.exit(0);
exitApplication();
} catch (Exception e) {
// something went wrong
Logger.getLogger().exception(null, "Failed to restart bot!", e, ApplicationHandler.class, true);
@@ -111,6 +104,7 @@ public class ApplicationHandler {
}
UpdateDisBotData.shutdown();
UpdateDisPwData.shutdown();
AnnouncementThreader.getThreader().shutdown();
TimeManager.getManager().shutdown();
DiscordAccountHandler.getHandler().shutdown();
DatabaseManager.getManager().disconnectFromMySQL();

View File

@@ -1,6 +1,5 @@
package com.cloudcraftgaming.discal.bot.internal.service;
import com.cloudcraftgaming.discal.bot.module.announcement.AnnouncementTask;
import com.cloudcraftgaming.discal.bot.module.misc.StatusChanger;
import java.util.ArrayList;
@@ -37,15 +36,15 @@ public class TimeManager {
public void init() {
Timer timer = new Timer(true);
timer.schedule(new StatusChanger(), 10 * 1000, 10 * 1000);
timers.add(timer);
Timer at = new Timer(true);
at.schedule(new AnnouncementTask(), 5 * 1000 * 60, 5 * 1000 * 60);
//Timer at = new Timer(true);
//at.schedule(new AnnouncementTask(), 5 * 1000 * 60, 5 * 1000 * 60);
//timers.add(at);
Timer cc = new Timer(true);
cc.schedule(new CreatorCleaner(), 60 * 1000 * 60, 60 * 1000 * 60);
timers.add(at);
timers.add(cc);
}
/**

View File

@@ -6,6 +6,7 @@ import com.cloudcraftgaming.discal.api.message.MessageManager;
import com.cloudcraftgaming.discal.bot.internal.network.discordbots.UpdateDisBotData;
import com.cloudcraftgaming.discal.bot.internal.network.discordpw.UpdateDisPwData;
import com.cloudcraftgaming.discal.bot.internal.service.TimeManager;
import com.cloudcraftgaming.discal.bot.module.announcement.AnnouncementThreader;
import com.cloudcraftgaming.discal.logger.Logger;
import sx.blah.discord.api.events.EventSubscriber;
import sx.blah.discord.handle.impl.events.ReadyEvent;
@@ -23,6 +24,9 @@ public class ReadyEventListener {
try {
TimeManager.getManager().init();
//Lets test the new announcement multi-threader...
AnnouncementThreader.getThreader().init();
UpdateDisBotData.init();
UpdateDisPwData.init();

View File

@@ -142,9 +142,9 @@ public class AnnouncementTask extends TimerTask {
if (difference < 0) {
//Event past, we can delete announcement depending on the type
if (a.getAnnouncementType() == AnnouncementType.SPECIFIC) {
if (a.getAnnouncementType() == AnnouncementType.SPECIFIC)
DatabaseManager.getManager().deleteAnnouncement(a.getAnnouncementId().toString());
}
return false;
} else {
return difference <= maxDifferenceMs;
@@ -152,32 +152,32 @@ public class AnnouncementTask extends TimerTask {
}
private long getEventStartMs(Event e) {
if (e.getStart().getDateTime() != null) {
if (e.getStart().getDateTime() != null)
return e.getStart().getDateTime().getValue();
} else {
else
return e.getStart().getDate().getValue();
}
}
private GuildSettings getSettings(Announcement a) {
if (!allSettings.containsKey(a.getGuildId())) {
if (!allSettings.containsKey(a.getGuildId()))
allSettings.put(a.getGuildId(), DatabaseManager.getManager().getSettings(a.getGuildId()));
}
return allSettings.get(a.getGuildId());
}
private CalendarData getCalendarData(Announcement a) {
if (!calendars.containsKey(a.getGuildId())) {
if (!calendars.containsKey(a.getGuildId()))
calendars.put(a.getGuildId(), DatabaseManager.getManager().getMainCalendar(a.getGuildId()));
}
return calendars.get(a.getGuildId());
}
private Calendar getService(GuildSettings gs) throws Exception {
if (gs.useExternalCalendar()) {
if (!customServices.containsKey(gs.getGuildID())) {
if (!customServices.containsKey(gs.getGuildID()))
customServices.put(gs.getGuildID(), CalendarAuth.getCalendarService(gs));
}
return customServices.get(gs.getGuildID());
}
return discalService;

View File

@@ -0,0 +1,214 @@
package com.cloudcraftgaming.discal.bot.module.announcement;
import com.cloudcraftgaming.discal.api.calendar.CalendarAuth;
import com.cloudcraftgaming.discal.api.database.DatabaseManager;
import com.cloudcraftgaming.discal.api.enums.announcement.AnnouncementType;
import com.cloudcraftgaming.discal.api.enums.event.EventColor;
import com.cloudcraftgaming.discal.api.object.GuildSettings;
import com.cloudcraftgaming.discal.api.object.announcement.Announcement;
import com.cloudcraftgaming.discal.api.object.calendar.CalendarData;
import com.cloudcraftgaming.discal.api.utils.EventUtils;
import com.cloudcraftgaming.discal.bot.utils.GuildUtils;
import com.cloudcraftgaming.discal.logger.Logger;
import com.google.api.client.util.DateTime;
import com.google.api.services.calendar.Calendar;
import com.google.api.services.calendar.model.Event;
import com.google.api.services.calendar.model.Events;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@SuppressWarnings({"WeakerAccess", "Duplicates"})
public class AnnouncementThread extends Thread {
private final AnnouncementType type;
private Calendar discalService;
private HashMap<Long, GuildSettings> allSettings = new HashMap<>();
private HashMap<Long, CalendarData> calendars = new HashMap<>();
private HashMap<Long, Calendar> customServices = new HashMap<>();
private HashMap<Long, List<Event>> allEvents = new HashMap<>();
public AnnouncementThread(AnnouncementType _type) {
type = _type;
}
@Override
public void run() {
Logger.getLogger().announcement("Starting announcement loop for type: " + type.name() + "!");
try {
//Get the default stuff.
try {
discalService = CalendarAuth.getCalendarService(null);
} catch (IOException e) {
Logger.getLogger().exception(null, "Failed to get service! 01a0101", e, this.getClass(), true);
}
//NOTE: This list EXCLUDES disabled announcements!!!!!!!
ArrayList<Announcement> allAnnouncements = DatabaseManager.getManager().getEnabledAnnouncements(type);
for (Announcement a: allAnnouncements) {
Logger.getLogger().announcement("starting an announcement", a.getGuildId() + "", a.getAnnouncementId() + "", "N/a");
//Check if guild is part of DisCal's guilds. This way we can clear out the database...
if (!GuildUtils.active(a.getGuildId())) {
DatabaseManager.getManager().deleteAnnouncement(a.getAnnouncementId().toString());
continue;
}
//Get everything we need ready.
GuildSettings settings = getSettings(a);
CalendarData calendar = getCalendarData(a);
Calendar service;
try {
service = getService(settings);
} catch (Exception e) {
Logger.getLogger().exception(null, "Failed to handle service! 01a102", e, this.getClass(), true);
continue;
}
//Now we can check the announcement type and do all the actual logic here.
switch (type) {
case SPECIFIC:
if (EventUtils.eventExists(settings, a.getEventId())) {
try {
Event e = service.events().get(calendar.getCalendarId(), a.getEventId()).execute();
if (inRange(a, e)) {
//We can announce it.
AnnouncementMessageFormatter.sendAnnouncementMessage(a, e, calendar, settings);
//And now lets delete it
DatabaseManager.getManager().deleteAnnouncement(a.getAnnouncementId().toString());
}
} catch (IOException e) {
//Event getting error, we know it exists tho
Logger.getLogger().exception(null, "Failed to get event! 01a103", e, this.getClass(), true);
}
} else {
//Event is gone, we can just delete this shit.
DatabaseManager.getManager().deleteAnnouncement(a.getAnnouncementId().toString());
}
break;
case UNIVERSAL:
for (Event e: getEvents(settings, calendar, service, a)) {
if (inRange(a, e)) {
//It fits! Let's do it!
AnnouncementMessageFormatter.sendAnnouncementMessage(a, e, calendar, settings);
}
}
break;
case COLOR:
for (Event e: getEvents(settings, calendar, service, a)) {
if (a.getEventColor() == EventColor.fromNameOrHexOrID(e.getColorId())) {
if (inRange(a, e)) {
//It fits! Let's do it!
AnnouncementMessageFormatter.sendAnnouncementMessage(a, e, calendar, settings);
}
}
}
break;
case RECUR:
for (Event e: getEvents(settings, calendar, service, a)) {
if (inRange(a, e)) {
if (e.getId().contains("_") && e.getId().split("_")[0].equals(a.getEventId())) {
//It fits! Lets announce!
AnnouncementMessageFormatter.sendAnnouncementMessage(a, e, calendar, settings);
}
}
}
break;
}
Logger.getLogger().announcement("finished an announcement", a.getGuildId() + "", a.getAnnouncementId() + "", "N/a");
}
//Just clear everything immediately.
allSettings.clear();
calendars.clear();
customServices.clear();
allEvents.clear();
Logger.getLogger().announcement("Finished announcement loop for type: " + type.name() + "!");
} catch (Exception e) {
Logger.getLogger().exception(null, "SOMETHING BAD IN THE ANNOUNCER!!!!! ANNOUNCEMENT TYPE: " + type.name(), e, this.getClass(), true);
//Clear everything because why take up RAM after is broke???
allSettings.clear();
calendars.clear();
customServices.clear();
allEvents.clear();
}
}
private boolean inRange(Announcement a, Event e) {
long maxDifferenceMs = 5 * 60 * 1000; //5 minutes
long announcementTimeMs = Integer.toUnsignedLong(a.getMinutesBefore() + (a.getHoursBefore() * 60)) * 60 * 1000;
long timeUntilEvent = getEventStartMs(e) - System.currentTimeMillis();
long difference = timeUntilEvent - announcementTimeMs;
if (difference < 0) {
//Event past, we can delete announcement depending on the type
if (a.getAnnouncementType() == AnnouncementType.SPECIFIC)
DatabaseManager.getManager().deleteAnnouncement(a.getAnnouncementId().toString());
return false;
} else {
return difference <= maxDifferenceMs;
}
}
private long getEventStartMs(Event e) {
if (e.getStart().getDateTime() != null)
return e.getStart().getDateTime().getValue();
else
return e.getStart().getDate().getValue();
}
private GuildSettings getSettings(Announcement a) {
if (!allSettings.containsKey(a.getGuildId()))
allSettings.put(a.getGuildId(), DatabaseManager.getManager().getSettings(a.getGuildId()));
return allSettings.get(a.getGuildId());
}
private CalendarData getCalendarData(Announcement a) {
if (!calendars.containsKey(a.getGuildId()))
calendars.put(a.getGuildId(), DatabaseManager.getManager().getMainCalendar(a.getGuildId()));
return calendars.get(a.getGuildId());
}
private Calendar getService(GuildSettings gs) throws Exception {
if (gs.useExternalCalendar()) {
if (!customServices.containsKey(gs.getGuildID()))
customServices.put(gs.getGuildID(), CalendarAuth.getCalendarService(gs));
return customServices.get(gs.getGuildID());
}
return discalService;
}
private List<Event> getEvents(GuildSettings gs, CalendarData cd, Calendar service, Announcement a) {
if (!allEvents.containsKey(gs.getGuildID())) {
Logger.getLogger().announcement("getting events for guild...", gs.getGuildID() + "", a.getAnnouncementId() + "", "N/a");
try {
Events events = service.events().list(cd.getCalendarAddress())
.setMaxResults(15)
.setTimeMin(new DateTime(System.currentTimeMillis()))
.setOrderBy("startTime")
.setSingleEvents(true)
.setShowDeleted(false)
.execute();
List<Event> items = events.getItems();
allEvents.put(gs.getGuildID(), items);
} catch (IOException e) {
Logger.getLogger().exception(null, "Failed to get events list! 01ae2304 | Guild: " + gs.getGuildID() + " | Announcement: " + a.getAnnouncementId(), e, this.getClass(), true);
return new ArrayList<>();
}
}
return allEvents.get(gs.getGuildID());
}
}

View File

@@ -0,0 +1,41 @@
package com.cloudcraftgaming.discal.bot.module.announcement;
import com.cloudcraftgaming.discal.api.enums.announcement.AnnouncementType;
import java.util.Timer;
import java.util.TimerTask;
public class AnnouncementThreader {
private static AnnouncementThreader threader;
private Timer timer;
private AnnouncementThreader() {
}
public static AnnouncementThreader getThreader() {
if (threader == null)
threader = new AnnouncementThreader();
return threader;
}
public void init() {
timer = new Timer(true);
timer.schedule(new TimerTask() {
@Override
public void run() {
for (AnnouncementType t: AnnouncementType.values()) {
AnnouncementThread at = new AnnouncementThread(t);
at.setDaemon(true);
at.run();
}
}
}, 5 * 1000 * 60, 5 * 1000 * 60);
}
public void shutdown() {
timer.cancel();
}
}