All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jsoftware.javamail.JavaMailJMSStatistics Maven / Gradle / Ivy

package org.jsoftware.javamail;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.mail.Address;
import javax.mail.Header;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.management.*;
import javax.management.openmbean.*;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.management.ManagementFactory;
import java.util.Date;
import java.util.Enumeration;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Ejb bean collecting JavaMail usage statistics and expose them via JMX.
 * @author szalik
 */
@Singleton(name = "JavaMailJMSStatisticsLocal")
@Startup
public class JavaMailJMSStatistics extends NotificationBroadcasterSupport implements JavaMailJMSStatisticsLocal, DynamicMBean {
    public static final String NOTIFICATION_TYPE_SUCCESS = "JavaMail-Send-Success";
    public static final String JAVA_MAIL_SEND_FAILURE = "JavaMail-Send-Failure";
    public static final ObjectName JMX_OBJECT_NAME;

    private MBeanServer platformMBeanServer;
    private static final MBeanInfo M_BEAN_INFO;
    private static final CompositeType ROW_HEADER_TYPE, ROW_ADDR_TYPE, MAIL_INFO_TYPE;
    private static final TabularType TAB_ADDR_TYPE, TAB_HEADER_TYPE;
    private Date startDate;
    private MessageAndAddresses lastSuccessMessage, lastFailMessage;
    private final AtomicLong successCounter = new AtomicLong(0), failureCounter = new AtomicLong(0), seq = new AtomicLong(0);

    static {
        try {
            JMX_OBJECT_NAME = new ObjectName("JavaMailJMS:type=" + JavaMailJMSStatistics.class.getName());
            ROW_HEADER_TYPE = new CompositeType("MailHeaders", "Mail headers",
                    new String[]{"header-name", "header-value"},
                    new String[]{"Name", "Value"},
                    new OpenType[]{SimpleType.STRING, SimpleType.STRING}
            );
            ROW_ADDR_TYPE = new CompositeType("MailAddress", "Mail single address",
                new String[]{"addressType", "address"},
                new String[]{"Address type", "Email address"},
                new OpenType[]{SimpleType.STRING, SimpleType.STRING}
            );
            TAB_ADDR_TYPE = new TabularType("Addresses", "Mail addresses", ROW_ADDR_TYPE, new String[] {"addressType", "address"});
            TAB_HEADER_TYPE = new TabularType("Headers", "Mail headers", ROW_HEADER_TYPE, new String[] {"header-name"});
            MAIL_INFO_TYPE = new CompositeType("MailInfo", "Mail info",
                    new String[]{"messageId", "date", "subject", "toAddresses", "headers", "errorDescription"},
                    new String[]{"Message ID", "Sent date", "Message subject", "Table of addresses", "Message headers", "Error description if any"},
                    new OpenType[]{SimpleType.STRING, SimpleType.DATE, SimpleType.STRING, TAB_ADDR_TYPE, TAB_HEADER_TYPE, SimpleType.STRING}
            );


            M_BEAN_INFO = new MBeanInfo(JavaMailJMSStatistics.class.getName(), "JavaMailJMS statistics",
                    new MBeanAttributeInfo[] {
                            new OpenMBeanAttributeInfoSupport("statisticsCollectionStartDate", "Start date", SimpleType.DATE, true, false, false),
                            new OpenMBeanAttributeInfoSupport("lastSuccessfulMailInfo", "Last successful message send", MAIL_INFO_TYPE, true, false, false),
                            new OpenMBeanAttributeInfoSupport("lastFailureMailInfo", "Last unsuccessful message send", MAIL_INFO_TYPE, true, false, false),
                            new OpenMBeanAttributeInfoSupport("countSuccessful", "Successful messages counter", SimpleType.LONG, true, false, false),
                            new OpenMBeanAttributeInfoSupport("countFailure", "Unsuccessful messages counter", SimpleType.LONG, true, false, false)
                    },
                    new MBeanConstructorInfo[0],
                    new MBeanOperationInfo[] { new OpenMBeanOperationInfoSupport("reset", "Reset statistics", new OpenMBeanParameterInfo[0], SimpleType.DATE, MBeanOperationInfo.ACTION)},
                    new MBeanNotificationInfo[] { new MBeanNotificationInfo(new String[]{"JavaMail-Send-Success", "JavaMail-Send-Failure"}, "mail-events", "Info about emails that has been sent.") }
            );
        } catch (OpenDataException e) {
            throw new RuntimeException("Cannot create openTypes", e);
        } catch (MalformedObjectNameException e) {
            throw new RuntimeException("Cannot create JMX Object Name", e);
        }
    }


    @PostConstruct
    public void registerInJMX() {
        try {
            reset();
            platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
            platformMBeanServer.registerMBean(this, JMX_OBJECT_NAME);
        } catch (Exception e) {
            throw new IllegalStateException("Problem during registration of JavaMailStatistics into JMX:" + e, e);
        }
    }

    @PreDestroy
    public void unregisterFromJMX() {
        try {
            platformMBeanServer.unregisterMBean(JMX_OBJECT_NAME);
        } catch (Exception e) {
            throw new IllegalStateException("Problem during unregistration of JavaMailStatistics from JMX:" + e, e);
        }
    }

    public void onSuccess(MimeMessage mimeMessage, Address[] addresses) {
        MessageAndAddresses maa = new MessageAndAddresses(mimeMessage, addresses, null);
        successCounter.incrementAndGet();
        lastSuccessMessage = maa;
        sendNotification(maa);
    }

    public void onFailure(MimeMessage mimeMessage, Address[] addresses, Exception ex) {
        MessageAndAddresses maa = new MessageAndAddresses(mimeMessage, addresses, ex);
        failureCounter.incrementAndGet();
        lastFailMessage = maa;
        sendNotification(maa);
    }

    private void reset() {
        startDate = new Date();
        lastFailMessage = null;
        lastSuccessMessage = null;
        successCounter.set(0);
        failureCounter.set(0);
    }

    private void sendNotification(MessageAndAddresses maa) {
        try {
            Notification notification;
            if (maa.getException() == null) {
                notification = new Notification(NOTIFICATION_TYPE_SUCCESS, maa.getMessage().getMessageID(), seq.incrementAndGet(), "Sent");
            } else {
                notification = new Notification(JAVA_MAIL_SEND_FAILURE, maa.getMessage().getMessageID(), seq.incrementAndGet(), "Exception:" + maa.getException());
            }
            StringBuilder sb = new StringBuilder();
            sb.append("Subject:").append(maa.getMessage().getSubject()).append('\n');
            for(Address address : maa.getAddresses()) {
                sb.append(address).append('\n');
            }
            notification.setUserData(sb.toString());
            sendNotification(notification);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    private static CompositeData convert(MessageAndAddresses maa) {
        if (maa == null) {
            return null;
        }
        try {
            TabularData addrData = new TabularDataSupport(TAB_ADDR_TYPE);
            for(Address addr : maa.getAddresses()) {
                addrData.put(new CompositeDataSupport(ROW_ADDR_TYPE, new String[]{"addressType", "address"}, new Object[]{addr.getType(), addr.toString()}));
            }
            TabularData headerData = new TabularDataSupport(TAB_HEADER_TYPE);
            Enumeration en = maa.getMessage().getAllHeaders();
            while (en.hasMoreElements()) {
                Header header = (Header) en.nextElement();
                headerData.put(new CompositeDataSupport(ROW_HEADER_TYPE, new String[]{"header-name", "header-value"}, new Object[]{header.getName(), header.getValue()}));
            }
            String error = null;
            if (maa.getException() != null) {
                StringWriter sw = new StringWriter();
                sw.append(maa.getException().toString());
                maa.getException().printStackTrace(new PrintWriter(sw));
                sw.flush();
                error = sw.toString();
            }
            return new CompositeDataSupport(MAIL_INFO_TYPE,
                    new String[] {"messageId", "date", "subject", "toAddresses", "headers", "errorDescription"},
                    new Object[]{maa.getMessage().getMessageID(), new Date(maa.getTimestamp()), maa.getMessage().getSubject(), addrData, headerData, error}
            );
        } catch (OpenDataException | MessagingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public AttributeList getAttributes(String[] attributeNames) {
        AttributeList resultList = new AttributeList();
        if (attributeNames.length == 0) {
            return resultList;
        }
        for (String attributeName : attributeNames) {
            try {
                Object value = getAttribute(attributeName);
                resultList.add(new Attribute(attributeName, value));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return resultList;
    }

    @Override
    public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException {
        if ("statisticsCollectionStartDate".equalsIgnoreCase(attribute)) {
            return startDate;
        }
        if ("lastSuccessfulMailInfo".equalsIgnoreCase(attribute)) {
            return convert(lastSuccessMessage);
        }
        if ("lastFailureMailInfo".equalsIgnoreCase(attribute)) {
            return convert(lastFailMessage);
        }
        if ("countSuccessful".equalsIgnoreCase(attribute)) {
            return successCounter.get();
        }
        if ("countFailure".equalsIgnoreCase(attribute)) {
            return failureCounter.get();
        }
        throw new AttributeNotFoundException("Attribute " + attribute + " not found");
    }

    @Override
    public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
        throw new InvalidAttributeValueException("Attribute " + attribute + " is read-only.");
    }

    @Override
    public AttributeList setAttributes(AttributeList attributes) {
        for(Attribute attribute : attributes.asList()) {
            try {
                setAttribute(attribute);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return attributes;
    }




    @Override
    public Object invoke(String actionName, Object[] params, String[] signature) throws MBeanException, ReflectionException {
        if ("reset".equalsIgnoreCase(actionName)) {
            reset();
            return startDate;
        }
        return null;
    }

    @Override
    public MBeanInfo getMBeanInfo() {
        return M_BEAN_INFO;
    }
}


class MessageAndAddresses {
    private final MimeMessage message;
    private final Address[] addresses;
    private final Exception exception;
    private final long timestamp;

    MessageAndAddresses(MimeMessage message, Address[] addresses, Exception exception) {
        this.message = message;
        this.addresses = addresses;
        this.exception = exception;
        this.timestamp = System.currentTimeMillis();
    }

    public Exception getException() {
        return exception;
    }

    public MimeMessage getMessage() {
        return message;
    }

    public long getTimestamp() {
        return timestamp;
    }

    public Address[] getAddresses() {
        return addresses;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy