//$Header: /cvsroot-fuse/mec-as2/39/mendelson/comm/as2/notification/Notification.java,v 1.1 2012/04/18 14:10:31 heller Exp $
package de.mendelson.comm.as2.notification;
import de.mendelson.comm.as2.AS2ServerVersion;
import de.mendelson.util.security.cert.CertificateManager;
import de.mendelson.util.security.cert.KeystoreCertificate;
import de.mendelson.comm.as2.log.LogAccessDB;
import de.mendelson.comm.as2.log.LogEntry;
import de.mendelson.comm.as2.message.AS2MessageInfo;
import de.mendelson.comm.as2.message.MessageAccessDB;
import de.mendelson.comm.as2.partner.Partner;
import de.mendelson.comm.as2.partner.PartnerAccessDB;
import de.mendelson.comm.as2.partner.PartnerCertificateInformation;
import de.mendelson.comm.as2.server.AS2Server;
import de.mendelson.comm.as2.statistic.QuotaAccessDB;
import de.mendelson.comm.as2.statistic.StatisticOverviewEntry;
import de.mendelson.util.MecResourceBundle;
import java.io.File;
import java.net.InetAddress;
import java.sql.Connection;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
/*
* Copyright (C) mendelson-e-commerce GmbH Berlin Germany
*
* This software is subject to the license agreement set forth in the license.
* Please read and agree to all terms before using this software.
* Other product and brand names are trademarks of their respective owners.
*/
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.mail.Address;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.SendFailedException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
/**
* Performs the notification for an event
* @author S.Heller
* @version $Revision: 1.1 $
*/
public class Notification {
private String templateDir = "notificationtemplates" + File.separator;
/**Stores the connection data and notification eMail*/
private NotificationData data;
private Logger logger = Logger.getLogger(AS2Server.SERVER_LOGGER_NAME);
/**localize your output*/
private MecResourceBundle rb = null;
//DB connection
private Connection configConnection;
private Connection runtimeConnection;
/**Will not perform a lookup in the db but take the passed notification data object
*/
public Notification(NotificationData data, Connection configConnection, Connection runtimeConnection) {
this.configConnection = configConnection;
this.runtimeConnection = runtimeConnection;
//Load resourcebundle
try {
this.rb = (MecResourceBundle) ResourceBundle.getBundle(
ResourceBundleNotification.class.getName());
} //load up resourcebundle
catch (MissingResourceException e) {
throw new RuntimeException("Oops..resource bundle " + e.getClassName() + " not found.");
}
this.data = data;
}
/**Constructure without notification data, will perform a lookup in the db*/
public Notification(Connection configConnection, Connection runtimeConnection) {
this.configConnection = configConnection;
this.runtimeConnection = runtimeConnection;
//Load resourcebundle
try {
this.rb = (MecResourceBundle) ResourceBundle.getBundle(
ResourceBundleNotification.class.getName());
} //load up resourcebundle
catch (MissingResourceException e) {
throw new RuntimeException("Oops..resource bundle " + e.getClassName() + " not found.");
}
//figure out the notification data
NotificationAccessDB access = new NotificationAccessDB(this.configConnection);
this.data = access.getNotificationData();
}
public void sendResendDetected(AS2MessageInfo newMessageInfo, AS2MessageInfo alreadyExistingMessageInfo,
Partner sender, Partner receiver) throws Exception{
//do only notify if this is requested by the config
if (!this.data.notifyResendDetected()) {
return;
}
String filename = this.getLocalizedTemplateFilename("template_notification_resend_detected");
Properties replacement = new Properties();
replacement.setProperty("${PRODUCTNAME}", AS2ServerVersion.getProductName());
replacement.setProperty("${HOST}", InetAddress.getLocalHost().getHostName());
replacement.setProperty("${EXISTING_MESSAGE_INIT_TIME}", alreadyExistingMessageInfo.getInitDate().toString());
replacement.setProperty("${MESSAGEID}", newMessageInfo.getMessageId());
replacement.setProperty("${SENDER}", sender.getName());
replacement.setProperty("${RECEIVER}", receiver.getName());
NotificationMail mail = new NotificationMail();
mail.read(filename, replacement);
this.sendMail(mail);
this.logger.fine(this.rb.getResourceString("misc.message.send", this.data.getNotificationMail()));
}
/**The system has imported a new certificate into the SSL keystore. The SSL connector
* has to be restarted before these changes are taken*/
public void sendSSLCertificateAddedByCEM(Partner partner, KeystoreCertificate cert) throws Exception {
//do only notify if this is requested by the config
if (!this.data.notifyCEM()) {
return;
}
String filename = this.getLocalizedTemplateFilename("template_notification_cem_ssl_cert_added");
Properties replacement = new Properties();
replacement.setProperty("${PRODUCTNAME}", AS2ServerVersion.getProductName());
replacement.setProperty("${HOST}", InetAddress.getLocalHost().getHostName());
replacement.setProperty("${PARTNER}", partner.getName());
StringBuilder techInfo = new StringBuilder();
String alias = cert.getAlias();
techInfo.append(alias).append(":\n");
for (int i = 0; i < alias.length() + 1; i++) {
techInfo.append("-");
}
techInfo.append("\n");
techInfo.append(cert.getInfo());
techInfo.append("\n");
replacement.setProperty("${CERTIFICATETECHDETAILS}", techInfo.toString());
NotificationMail mail = new NotificationMail();
mail.read(filename, replacement);
this.sendMail(mail);
this.logger.fine(this.rb.getResourceString("misc.message.send", this.data.getNotificationMail()));
}
/**Sends a notification if the send quota has been exceeded
*
* @param partner
*/
public void sendPartnerSendQuotaExceeded(Partner localStation, Partner partner) {
StatisticOverviewEntry entry = null;
try {
QuotaAccessDB access = new QuotaAccessDB(this.configConnection, this.runtimeConnection );
entry = access.getStatisticOverview(
localStation.getAS2Identification(), partner.getAS2Identification());
if (partner.isNotifySendEnabled() && partner.getNotifySend() == entry.getSendMessageCount()) {
String filename = this.getLocalizedTemplateFilename("template_notification_sendquota_exceeded");
Properties replacement = new Properties();
replacement.setProperty("${PRODUCTNAME}", AS2ServerVersion.getProductName());
replacement.setProperty("${HOST}", InetAddress.getLocalHost().getHostName());
replacement.setProperty("${PARTNER}", partner.getName());
replacement.setProperty("${QUOTA}", String.valueOf(partner.getNotifySend()));
NotificationMail mail = new NotificationMail();
mail.read(filename, replacement);
this.sendMail(mail);
this.logger.fine(this.rb.getResourceString("quota.send.message.send", this.data.getNotificationMail()));
}
} catch (Exception e) {
return;
}
}
/**Sends a notification if the receive quota has been exceeded
*
* @param partner
*/
public void sendPartnerReceiveQuotaExceeded(Partner localStation, Partner partner) {
StatisticOverviewEntry entry = null;
try {
QuotaAccessDB access = new QuotaAccessDB(this.configConnection, this.runtimeConnection );
entry = access.getStatisticOverview(
localStation.getAS2Identification(), partner.getAS2Identification());
if (partner.isNotifyReceiveEnabled() && partner.getNotifyReceive() == entry.getReceivedMessageCount()) {
String filename = this.getLocalizedTemplateFilename("template_notification_receivequota_exceeded");
Properties replacement = new Properties();
replacement.setProperty("${PRODUCTNAME}", AS2ServerVersion.getProductName());
replacement.setProperty("${HOST}", InetAddress.getLocalHost().getHostName());
replacement.setProperty("${PARTNER}", partner.getName());
replacement.setProperty("${QUOTA}", String.valueOf(partner.getNotifyReceive()));
NotificationMail mail = new NotificationMail();
mail.read(filename, replacement);
this.sendMail(mail);
this.logger.fine(this.rb.getResourceString("quota.receive.message.send", this.data.getNotificationMail()));
}
} catch (Exception e) {
return;
}
}
/**Convinience method to send a notification to the administrator that something unexpected happened to the system*/
public static void systemFailure(Connection configConnection, Connection runtimeConnection, Throwable exception) {
Notification notification = new Notification(configConnection, runtimeConnection);
notification.sendSystemFailure(exception);
}
/**Sends a notification to the administrator that something unexpected happened to the system*/
public void sendSystemFailure(Throwable exception) {
//do only notify if this is requested by the config
if (!this.data.notifySystemFailure()) {
return;
}
try {
String filename = this.getLocalizedTemplateFilename("template_notification_systemproblem");
Properties replacement = new Properties();
replacement.setProperty("${PRODUCTNAME}", AS2ServerVersion.getProductName());
replacement.setProperty("${HOST}", InetAddress.getLocalHost().getHostName());
replacement.setProperty("${MESSAGE}", exception.getClass().getName()
+ ": " + exception.getMessage());
StackTraceElement[] trace = exception.getStackTrace();
StringBuilder builder = new StringBuilder();
for (StackTraceElement element : trace) {
builder.append(element.toString());
builder.append("\n");
}
replacement.setProperty("${DETAILS}", builder.toString());
NotificationMail mail = new NotificationMail();
mail.read(filename, replacement);
this.sendMail(mail);
this.logger.fine(this.rb.getResourceString("misc.message.send", this.data.getNotificationMail()));
} catch (Exception e) {
return;
}
}
/**Sends a notification if the receive quota has been exceeded
*
* @param partner
*/
public void sendPartnerSendReceiveQuotaExceeded(Partner localStation, Partner partner) {
StatisticOverviewEntry entry = null;
try {
QuotaAccessDB access = new QuotaAccessDB(this.configConnection, this.runtimeConnection );
entry = access.getStatisticOverview(
localStation.getAS2Identification(), partner.getAS2Identification());
if (partner.isNotifySendReceiveEnabled() && partner.getNotifySendReceive() == entry.getSendMessageCount() + entry.getReceivedMessageCount()) {
String filename = this.getLocalizedTemplateFilename("template_notification_sendreceivequota_exceeded");
Properties replacement = new Properties();
replacement.setProperty("${PRODUCTNAME}", AS2ServerVersion.getProductName());
replacement.setProperty("${HOST}", InetAddress.getLocalHost().getHostName());
replacement.setProperty("${PARTNER}", partner.getName());
replacement.setProperty("${QUOTA}", String.valueOf(partner.getNotifyReceive()));
NotificationMail mail = new NotificationMail();
mail.read(filename, replacement);
this.sendMail(mail);
this.logger.fine(this.rb.getResourceString("quota.sendreceive.message.send", this.data.getNotificationMail()));
}
} catch (Exception e) {
return;
}
}
/**Adds a _de _fr etc to the template name and returns it*/
private String getLocalizedTemplateFilename(String templateName) {
String language = Locale.getDefault().getLanguage();
//select language specific template
if (new File(this.templateDir + templateName + "_" + language).exists()) {
templateName = this.templateDir + templateName + "_" + language;
} else {
templateName = this.templateDir + templateName;
}
return (templateName);
}
/**Sends a test notification
*
*/
public void sendTest() throws Exception {
String filename = this.getLocalizedTemplateFilename("template_notification_test");
Properties replacement = new Properties();
replacement.setProperty("${PRODUCTNAME}", AS2ServerVersion.getProductName());
replacement.setProperty("${HOST}", InetAddress.getLocalHost().getHostName());
replacement.setProperty("${USER}", System.getProperty("user.name"));
NotificationMail mail = new NotificationMail();
mail.read(filename, replacement);
this.sendMail(mail);
this.logger.fine(this.rb.getResourceString("test.message.send", this.data.getNotificationMail()));
}
/**Sends an email that a certification will expire*/
public void sendCertificateWillExpire(KeystoreCertificate certificate, int expireDuration) throws Exception {
//do only notify if this is requested by the config
if (!this.data.notifyCertExpire()) {
return;
}
String templateName = "template_notification_cert_expire";
if (expireDuration <= 0) {
templateName = templateName + "d";
}
String filename = this.getLocalizedTemplateFilename(templateName);
Properties replacement = new Properties();
replacement.setProperty("${PRODUCTNAME}", AS2ServerVersion.getProductName());
replacement.setProperty("${HOST}", InetAddress.getLocalHost().getHostName());
if (expireDuration >= 0) {
replacement.setProperty("${DURATION}", String.valueOf(expireDuration));
}
replacement.setProperty("${ALIAS}", certificate.getAlias());
NotificationMail mail = new NotificationMail();
mail.read(filename, replacement);
this.sendMail(mail);
this.logger.fine(this.rb.getResourceString("cert.message.send",
new Object[]{this.data.getNotificationMail(),
certificate.getAlias()
}));
}
/**Sends an email that a CEM has been received*/
public void sendCEMRequestReceived(Partner initiator) throws Exception {
//do only notify if this is requested by the config
if (!this.data.notifyCEM()) {
return;
}
String filename = this.getLocalizedTemplateFilename("template_notification_cem_request_received");
Properties replacement = new Properties();
replacement.setProperty("${PRODUCTNAME}", AS2ServerVersion.getProductName());
replacement.setProperty("${HOST}", InetAddress.getLocalHost().getHostName());
replacement.setProperty("${PARTNER}", initiator.getName());
NotificationMail mail = new NotificationMail();
mail.read(filename, replacement);
this.sendMail(mail);
this.logger.fine(this.rb.getResourceString("misc.message.send",
this.data.getNotificationMail()));
}
/**Sends an email that a CEM has been received
*/
public void sendCertificateChangedByCEM(CertificateManager manager, Partner partner, int category) throws Exception {
//do only notify if this is requested by the config
if (!this.data.notifyCEM()) {
return;
}
String filename = this.getLocalizedTemplateFilename("template_notification_cem_cert_changed");
Properties replacement = new Properties();
replacement.setProperty("${PRODUCTNAME}", AS2ServerVersion.getProductName());
replacement.setProperty("${HOST}", InetAddress.getLocalHost().getHostName());
String description = partner.getPartnerCertificateInformationList().getCertificatePurposeDescription(
manager, partner, category);
replacement.setProperty("${CERTIFICATEDESCRIPTION}", description);
StringBuilder techInfo = new StringBuilder();
PartnerCertificateInformation info1 = partner.getCertificateInformation(category, 1);
PartnerCertificateInformation info2 = partner.getCertificateInformation(category, 2);
if (info1 != null) {
KeystoreCertificate certificate = manager.getKeystoreCertificateByFingerprintSHA1(info1.getFingerprintSHA1());
if (certificate != null) {
String alias = certificate.getAlias();
techInfo.append(alias).append(":\n");
for (int i = 0; i < alias.length() + 1; i++) {
techInfo.append("-");
}
techInfo.append("\n");
techInfo.append(certificate.getInfo());
techInfo.append("\n");
}
}
if (info2 != null) {
KeystoreCertificate certificate = manager.getKeystoreCertificateByFingerprintSHA1(info2.getFingerprintSHA1());
if (certificate != null) {
String alias = certificate.getAlias();
techInfo.append(alias).append(":\n");
for (int i = 0; i < alias.length() + 1; i++) {
techInfo.append("-");
}
techInfo.append("\n");
techInfo.append(certificate.getInfo());
techInfo.append("\n");
}
}
replacement.setProperty("${CERTIFICATETECHDETAILS}", techInfo.toString());
NotificationMail mail = new NotificationMail();
mail.read(filename, replacement);
this.sendMail(mail);
this.logger.fine(this.rb.getResourceString("misc.message.send",
this.data.getNotificationMail()));
}
/**Returns the message info object a a passed message id from the database*/
private AS2MessageInfo getMessageInfo(String messageId) throws Exception {
MessageAccessDB messageAccess
= new MessageAccessDB(this.configConnection, this.runtimeConnection);
AS2MessageInfo info = messageAccess.getLastMessageEntry(messageId);
if (info == null) {
throw new Exception("No message entry found for " + messageId);
}
return (info);
}
/**Sends an email that an error occured in a transaction
*/
public void sendTransactionError(String messageId) {
//do only notify if this is requested by the config
if (!this.data.notifyTransactionError()) {
return;
}
AS2MessageInfo info = null;
try {
//get additional properties for the notification eMail
String senderName = "Unknown";
String receiverName = "Unknown";
info = this.getMessageInfo(messageId);
//lookup partner
PartnerAccessDB partnerAccess = new PartnerAccessDB(this.configConnection, this.runtimeConnection);
if (info.getSenderId() != null) {
Partner sender = partnerAccess.getPartner(info.getSenderId());
if (sender != null) {
senderName = sender.getName();
}
}
if (info.getReceiverId() != null) {
Partner receiver = partnerAccess.getPartner(info.getReceiverId());
if (receiver != null) {
receiverName = receiver.getName();
}
}
LogAccessDB logAccess = new LogAccessDB(this.configConnection, this.runtimeConnection);
StringBuilder log = new StringBuilder();
LogEntry[] entries = logAccess.getLog(messageId);
DateFormat format = DateFormat.getDateTimeInstance();
for (LogEntry entry : entries) {
if (log.length() > 0) {
log.append("\n");
}
log.append("[").append(format.format(new Date(entry.getMillis()))).append("] ");
log.append(entry.getMessage());
}
String filename = this.getLocalizedTemplateFilename("template_notification_transaction_error");
Properties replacement = new Properties();
replacement.setProperty("${PRODUCTNAME}", AS2ServerVersion.getProductName());
replacement.setProperty("${HOST}", InetAddress.getLocalHost().getHostName());
replacement.setProperty("${MESSAGEID}", messageId);
replacement.setProperty("${SENDER}", senderName);
replacement.setProperty("${RECEIVER}", receiverName);
replacement.setProperty("${LOG}", log.toString());
if( info.getSubject() != null ){
replacement.setProperty("${SUBJECT}", info.getSubject());
}else{
replacement.setProperty("${SUBJECT}", "");
}
NotificationMail mail = new NotificationMail();
mail.read(filename, replacement);
this.sendMail(mail);
this.logger.log(Level.FINE, this.rb.getResourceString("transaction.message.send",
new Object[]{
info.getMessageId(),
this.data.getNotificationMail(),}), info);
} catch (Exception e) {
if (info == null) {
this.logger.severe(this.rb.getResourceString("transaction.message.send.error",
new Object[]{
messageId,
this.data.getNotificationMail(),
e.getMessage()
}));
} else {
e.printStackTrace();
this.logger.log(Level.SEVERE, this.rb.getResourceString("transaction.message.send.error",
new Object[]{
messageId,
this.data.getNotificationMail(),
e.getMessage()
}), info);
}
}
}
/**Returns the default session for the mail send process
*/
private Session getDefaultSession() {
Properties properties = new Properties();
properties.put("mail.smtp.host", this.data.getMailServer());
properties.put("mail.smtp.port", String.valueOf(this.data.getMailServerPort()));
properties.put("mail.transport.protocol", "smtp");
Session session = null;
if (this.data.isUseSMTHAuth()) {
properties.put("mail.smtp.auth", "true");
session = Session.getInstance(properties,
new SendMailAuthenticator(this.data.getSmtpUser(),
String.valueOf(this.data.getSmtpPass())));
} else {
session = Session.getInstance(properties, null);
}
return (session);
}
@SuppressWarnings("static-access")
private void sendMail(NotificationMail mail) throws Exception {
Session session = this.getDefaultSession();
// construct the message
MimeMessage msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(data.getReplyTo()));
msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(data.getNotificationMail(), false));
msg.setSubject(mail.getSubject());
msg.setText(mail.getBody());
msg.setSentDate(new Date());
msg.setHeader("X-Mailer", AS2ServerVersion.getProductName());
// send the message
Transport transport = null;
try {
transport = session.getTransport("smtp");
transport.send(msg);
} catch (SendFailedException sendFailedException) {
Address failedAddresses[] = sendFailedException.getInvalidAddresses();
StringBuilder errorMessage = new StringBuilder();
if (failedAddresses != null) {
errorMessage.append("The following mail addresses are invalid:").append("\n");
for (Address address : failedAddresses) {
errorMessage.append(address.toString()).append("\n");
}
}
Address validUnsentAddresses[] = sendFailedException.getValidUnsentAddresses();
if (validUnsentAddresses != null) {
errorMessage.append("No mail has been sent to the following valid addresses:").append("\n");
for (Address address : validUnsentAddresses) {
errorMessage.append(address.toString()).append("\n");
}
}
throw (sendFailedException);
} finally {
transport.close();
}
}
/**Used for the SMTP authentication, this is required by some mail servers*/
private static class SendMailAuthenticator extends Authenticator {
private String user;
private String password;
public SendMailAuthenticator(String user, String password) {
this.user = user;
this.password = password;
}
@Override
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(
this.user, this.password);
}
}
}