package org.ngbw.utils; import java.io.IOException; import java.util.Date; import java.util.Iterator; import java.util.List; import javax.mail.MessagingException; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.log4j.Logger; import org.ngbw.sdk.Workbench; import org.ngbw.sdk.common.util.StringUtils; import org.ngbw.sdk.database.SuTransactionType; import org.ngbw.sdk.database.User; import org.ngbw.sdk.database.UserSuAllocationService; import org.ngbw.sdk.database.UserSuAllocationService.UserSuAllocation; import org.ngbw.sdk.tool.CipresNotifier; import org.ngbw.sdk.tool.DateUtils; import org.ngbw.sdk.tool.Email; import org.ngbw.sdk.tool.EmailUtils; import org.ngbw.sdk.tool.Postman; /** * A simple class checks expiration date of user SU allocation records and sends out * alert email to the user if their record will expire in 30 days. * *

* An alert email is sent out iff the record will expire in exactly 30 days. * * @author Tony Chen * * @version 1.0.0 */ public class UserSuSubscriptionExpirationAlert { private final Logger logger = Logger.getLogger(UserSuSubscriptionExpirationAlert.class); private static final String NL = "\n"; private static final String THE_VERSION = "1.4.0"; // ====================[ Commandline Functions ]==================== // private static final String EMAIL = "email"; private static final String DRYRUN = "dryrun"; // ====================[ Commandline Options ]==================== // private static final String HELP = "help"; private static final String USAGE = "usage"; private static final String VERBOSE = "verbose"; private static final String VERSION = "version"; // ====================[ Local Attributes ]==================== // private static CommandLine cmdln = null; private static List argsList = null; private static boolean verbose = false; public UserSuSubscriptionExpirationAlert () {} private static synchronized CommandLine getCommandLine ( final String[] args ) throws ParseException { CommandLineParser parser = new DefaultParser(); return parser.parse(buildOptions(), args, Boolean.FALSE); } private static synchronized Options toOptions ( final Options ... optionsItems ) { final Options combined = new Options(); if (optionsItems != null && optionsItems.length > 0) { for (Options options : optionsItems) { for (Option option : options.getOptions()) { combined.addOption(option); } } } return combined; } private static synchronized Options buildBasicOptions () { final Options optionGroup = new Options(); optionGroup.addOption(Option.builder(EMAIL) .hasArg(true).argName("email-address") .desc("One or more email addresses to send reports when the run is complete.") .build()); optionGroup.addOption(Option.builder(DRYRUN) .hasArg(false) .desc("Dry run only, will not send email alert.") .build()); return optionGroup; } private static synchronized Options buildCommonOptions () { final Options optionGroup = new Options(); optionGroup.addOption(Option.builder(HELP).hasArg(false) .desc("Print help information.").build()); optionGroup.addOption(Option.builder(USAGE).hasArg(false) .desc("Print usage information.").build()); optionGroup.addOption(Option.builder(VERBOSE).hasArg(false) .desc("Output detail information.").build()); optionGroup.addOption(Option.builder(VERSION).hasArg(false) .desc("Show version and description.").build()); return optionGroup; } private static synchronized Options buildOptions () { return toOptions(buildBasicOptions(), buildCommonOptions()); } private void allUsages () { HelpFormatter formatter = new HelpFormatter(); System.out.println(); System.out.println("Commons:"); System.out.println("--------"); formatter.printHelp( UserSuAllocationRecordMaintenance.class.getSimpleName(), toOptions(buildOptions()), Boolean.FALSE); } private void printUsage ( final Options options ) { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp( UserSuAllocationRecordMaintenance.class.getSimpleName(), options, Boolean.FALSE); } private void printHelp () { String message = null; final String baseCommand = "> java -cp [classpath-to-this-jar] " + UserSuAllocationRecordMaintenance.class.getName() + " "; System.out.println(); printVersion(); System.out.println(); System.out.println("Run as:"); System.out.println(baseCommand + "[options-and-arguments ...]"); // ================================================== // System.out.println(); System.out.println("Common Options:"); System.out.println("---------------"); printUsage(toOptions(buildOptions())); System.out.println(); System.out.println("Examples:"); System.out.println(" > UserSuSubscriptionExpirationAlert -verbose"); // ================================================== // } private void printVersion () { System.out.println(this.getClass().getSimpleName() + " " + THE_VERSION); } private void printToStdOut ( final String message ) { if (verbose) { System.out.println(message); } } private void printToStdErr ( final String message ) { if (verbose) { System.err.println(message); } } public static void main ( String[] args ) { int status = 0; UserSuSubscriptionExpirationAlert subscriptionExpiration = null; try { Workbench.getInstance(); subscriptionExpiration = new UserSuSubscriptionExpirationAlert(); cmdln = getCommandLine(args); argsList = cmdln.getArgList(); //if (cmdln.hasOption(VERBOSE)) { verbose = true; //} if (cmdln.hasOption(USAGE)) { subscriptionExpiration.allUsages(); } else if (cmdln.hasOption(HELP)) { subscriptionExpiration.printHelp(); } else if (cmdln.hasOption(VERSION)) { subscriptionExpiration.printVersion(); } else { subscriptionExpiration.checkExpirationDates(); } } catch ( Throwable t ) { status = 1; System.err.println(t.getMessage()); Logger.getLogger(UserSuSubscriptionExpirationAlert.class).error(t.getMessage(), t); CipresNotifier.emailSysAdmin( Workbench.getInstance().getPortalName(""), null, // Request URI UserSuSubscriptionExpirationAlert.class, null, // user null, // IP address t.getMessage(), t); } subscriptionExpiration = null; System.exit(status); } private void checkExpirationDates () throws Throwable { try { final boolean isDryRun = cmdln.hasOption(DRYRUN); final String WILL_EXPIRE = "SU allocation will expire on [%s] in [%d] days."; final String HAS_EXPIRED = "SU allocation has expired on [%s], [%d] days ago."; // Retrieve ALL SU Allocations which expire ON or AFTER today. // Ignore already expired ones. final List nonExpiredAllocations = UserSuAllocation.filterByExpireTime( null, DateUtils.setTimeToBeginOfDay(new Date()), // expire time on or after now null, // expireTimeTo UserSuAllocationService.SORT_ASCENDING); if (nonExpiredAllocations != null && !nonExpiredAllocations.isEmpty()) { int emailsSent = 0; final Iterator itr = nonExpiredAllocations.iterator(); while (itr != null && itr.hasNext()) { final UserSuAllocation allocation = itr.next(); final long subscriptionsFromModifiedMethod = UserSuAllocationService.UserSuAllocationTransaction .retrieveTotalSuTransactions( allocation.getUserId(), SuTransactionType.PURCHASE, allocation.getStartTime(), allocation.getSuExpireTime()); // We only want to send out alerts for users who subscribe. // Do NOT send out alerts to users who only have free 1K SU because // they will get another 1K SU the next time they log in. // Free 1K SU is awarded to all users annually regardless of subscription. final long subscriptions = UserSuAllocationService.UserSuAllocationTransaction .retrieveTotalSuTransactions( allocation.getUserId(), allocation.getStartTime(), allocation.getSuExpireTime(), SuTransactionType.PURCHASE); String msg = ""; msg += String.format( "ID=[%d] User=[%d - %s] Subsrpt=[%d] SubsrptOLD=[%d] " + "AllocStart=[%s] AllocExpire=[%s].", allocation.getId(), allocation.getUser().getUserId(), allocation.getUser().getUsername(), subscriptions, subscriptionsFromModifiedMethod, DateUtils.formatDate("yyyy/MM/dd HH:mm:ss", allocation.getStartTime()), DateUtils.formatDate("yyyy/MM/dd HH:mm:ss", allocation.getSuExpireTime())); final Date expiresDate = allocation.getSuExpireTime(); final long daysLeft = DateUtils.daysDidff(expiresDate, new Date(), false); if (daysLeft > 0) { msg += "\n" + String.format(WILL_EXPIRE, DateUtils.formatDate("yyyy/MM/dd HH:mm:ss", expiresDate), daysLeft); } else { msg = "\n" + String.format(HAS_EXPIRED, DateUtils.formatDate("yyyy/MM/dd HH:mm:ss", expiresDate), Math.abs(daysLeft)); } if (subscriptions == 0) { msg += " No subscription, only free 1K SU."; } else { msg += String.format(" Subscription=[%d]", subscriptions); } final boolean willSendAlert = (subscriptions > 0 && daysLeft == 30); msg += "\n" + String.format(" Will send alert? [%s]", (willSendAlert) ? "YES" : "NO"); printToStdOut("\n" + msg); logger.debug("\n" + msg); // Send a reminder email to the user iff the allocation will expires in // exactly 30 days. if (subscriptions > 0 && daysLeft == 30 && !isDryRun) { emailUser(allocation, daysLeft); emailsSent += 1; } } final String message = String.format( "Total %d SU allocations inspected for expiring subscriptions." + "\n\n" + "Total %d emails sent.", nonExpiredAllocations.size(), emailsSent); logger.debug(message); // Email the result if instructed. // final String emails = cmdln.getOptionValue(EMAIL); if (!StringUtils.isNullOrEmpty(emails, true)) { emailReports( Workbench.getInstance().getEmailReplyAddress(), // sender's email Workbench.getInstance().getEmailSenderName(), // sender's name emails, String.format( "[%s] Expiring SU Subscriptions Alert Sent", Workbench.getInstance().getPortalName("")), message, false); } } else { System.err.println("Non-expired SU allocations not found!"); } } catch ( Throwable t ) { System.err.println("ERROR: " + StringUtils.coalesceNotNullNotEmpty( t.getMessage(), t.toString(), ExceptionUtils.getStackTrace(t))); logger.error(t.getMessage(), t); throw t; } } private void emailUser ( final UserSuAllocation allocation, final long dayCount ) { try { final User user = allocation.getUser(); /** * Email body template: * * Dear , * * Your current subscription to the CIPRES Science Gateway will expire in days. * Any time remaining in your account above 1,000 hours will be lost at that time. * Please contact me at cipresadmin@sdsc.edu if you have any questions. * * Sincerely, * Mark Miller */ final String body = String.format( "Dear %s," + "\n\n" + "Your current subscription to the CIPRES Science Gateway will expire in %d days. " + "\n" + "Any time remaining in your account above 1,000 hours will be lost at that time." + "\n" + "Please contact me at cipresadmin@sdsc.edu if you have any questions." + "\n\n" + "Sincerely," + "\n" + "Mark Miller", user.getUsername(), dayCount); String msg = String.format( "Alert sent to User=[%d - %s] Email[%s].", allocation.getUserId(), allocation.getUser().getUsername(), allocation.getUser().getEmail()); CipresNotifier.emailUser( allocation.getUser(), "Alert: Your current CIPRES subscription will expire soon!", body, false); logger.debug(msg); printToStdOut(msg); } catch ( Throwable t ) { logger.error(t.getMessage(), t); } } protected void emailReports ( final String senderEmail, final String senderName, final String recipients, // recipients' email addresses (1 or more) final String subject, final String content, final boolean htmlContent ) throws IOException, MessagingException { try { final String[] toEmails = EmailUtils.toEmailsArray(recipients); final Email email = new Email(); email.setEmailSubject(subject); email.addEmailContent(content, htmlContent); email.addToRecipients(EmailUtils.getInternetAddresses(true, toEmails)); email.setSender(senderEmail, senderName); Postman.deliver(Workbench.getInstance().getEmailConfigs(), email); } catch ( IOException | MessagingException t ) { throw t; } } }