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;
}
}
}