SMIME Mail Signing and Encryption using SMTP, IMAP - Yash-777/java-utils-mail-smime GitHub Wiki

Example

package com.java.mail;

import java.io.*;
import java.security.*;
import java.text.*;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.*;
import javax.mail.internet.*;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.java.mail.common.ByteArrayDataSource;
import com.sun.mail.smtp.SMTPTransport;

import net.markenwerk.utils.mail.smime.SmimeKey;
import net.markenwerk.utils.mail.smime.SmimeState;
import net.markenwerk.utils.mail.smime.SmimeUtil;

public class SMIMEMial_Util {
    private final static Log log = LogFactory.getLog(SMIMEMial_Util.class);
    
    // Sign with PrivateKey(Include PrivateKey Public Certificate), Encrypt with Public Key
    public static String security_privatekey_Sign = "Baeldung.p12", passwordCert_Sign ="password",
            security_certificate_Encrypt = "Baeldung.cer";
    
    public static String security_privatekey_DEcrypt = "Baeldung.p12", passwordCert_Decrypt ="password",
            security_certificate_VerifySign = "Baeldung.cer";
    
    static String outputMailFile = "C:/Yash/Mail/SendingMessage_<CurrDate>.eml";// .eml .msg .txt
    static String emlReadFile = "C:/Yash/Mail/SendingMessage_20210212_094108.eml";  // .msg, .eml
    
    
    public boolean isSigned, isEncrypted, isStoreMessage;
    public SMIMEMial_Util (boolean isSigned, boolean isEncrypted, boolean isStoreMessage) {
        this.isSigned = isSigned; this.isEncrypted = isEncrypted; this.isStoreMessage = isStoreMessage;
    }
    
    public X509Certificate cert, recipientPrivateKeyCert;
    public PrivateKey recipientPrivateKey;
    public void setCets(InputStream privateKeyStream, String passwordCert, InputStream certStream) throws Exception {
        if (isEncrypted) {
            if (null == certStream) throw new Exception("Empty Stream for Encrypt Mail");
            
            cert = getX509Certificate(certStream);
        }
        
        if (isSigned) {
            if (null == privateKeyStream) {
                throw new Exception("Empty Stream to Signing Mail");
            } else if (passwordCert == null || passwordCert == "") {
                throw new Exception("Empty password to Read the Private Key to Signing Mail");
            }
            recipientPrivateKey = getPrivateCertificate(privateKeyStream, passwordCert);
        }
    }
    public void setCets_IMAP(InputStream privateKeyStream, String passwordCert, InputStream certStream) throws Exception {
        if (isEncrypted) {
            if (null == privateKeyStream) {
                throw new Exception("Empty Stream to Signing Mail");
            } else if (passwordCert == null || passwordCert == "") {
                throw new Exception("Empty password to Read the Private Key to Signing Mail");
            }
            recipientPrivateKey = getPrivateCertificate(privateKeyStream, passwordCert);
        }
        
        if (isSigned) {
            if (null == certStream) throw new Exception("Empty Stream for Encrypt Mail");
            
            cert = getX509Certificate(certStream);
        }
    }
    
    private static DateFormat dateFormat = null;
    private static String reportDate, timeZone = "CET";;
    private static final String DATEPATTERN_STR = "yyyyMMdd_HHmmss";
    
    public Session session_IMAP;
    String sftpHost, sftpPort;
    boolean isSmtpAuth, doSSL;
    String smtpAuthLogin, smtpAuthPassword, SSLSOCKETFACTORYClass;
    
    public void setProperties(String sftpHost, String sftpPort,
            boolean isSmtpAuth, String userLogin, String userPassword,
            boolean doSSL, String SSLSOCKETFACTORYClass) {
        this.sftpHost = sftpHost;
        this.sftpPort = sftpPort;
        
        this.isSmtpAuth = isSmtpAuth;
        this.smtpAuthLogin = userLogin; this.smtpAuthPassword = userPassword;
        this.doSSL = doSSL;
        this.SSLSOCKETFACTORYClass = SSLSOCKETFACTORYClass;
    }
    public Properties getPropertiesSFTP() {
        Properties props = new Properties();
        props.put("mail.smtp.host", sftpHost);
        props.put("mail.smtp.port", sftpPort);
        
        if (isSmtpAuth) {
            props.put("mail.smtp.auth", "true");
            props.put("mail.smtp.user", smtpAuthLogin);
            props.put("mail.smtp.password", smtpAuthPassword);
        }
        if (doSSL) {
            props.put("mail.smtp.socketFactory.class", SSLSOCKETFACTORYClass);
            props.put("mail.smtp.socketFactory.fallback", "false");
        }
        return props;
    }
    public static Properties getPropertiesIMAP(String protocol, int port) {
        Properties props = new Properties();
        props = System.getProperties();
        if (protocol.equalsIgnoreCase("IMAP") || protocol.equalsIgnoreCase("IMAPS")) {
            props.setProperty("mail.imap.partialfetch", "false");
            
            if (protocol.equalsIgnoreCase("IMAPS")) {
                props.setProperty("mail.imap.ssl.enable", "true");
            } else {
                props.setProperty("mail.imap.ssl.enable", "false");
            }
            
            // set this session up to use SSL for IMAP connections
            props.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
            // don't fallback to normal IMAP connections on failure.
            props.setProperty("mail.imap.socketFactory.fallback", "false");
            props.setProperty("mail.imap.socketFactory.port", port + "");// S/IMAP port for imap/ssl connections.
            
            props.setProperty("mail.imap.auth.plain.disable", "true");
            //props.setProperty("mail.imaps.auth.plain.disable", "true");
        }
        return props;
    }
    
    
    public Message[] getMessages(boolean isStoreMessage,
            String protocol, String server, int port, String userName, String password, String mailBoxFolder, int readCount) throws MessagingException, FileNotFoundException {
        if (isStoreMessage) { // Default Mail Server properties
            File emlFile = new File(emlReadFile);
            InputStream source = new FileInputStream(emlFile);
            
            Properties props = System.getProperties();
            Session session_IMAP = Session.getDefaultInstance(props, null);
            MimeMessage message_IMAP = new MimeMessage(session_IMAP, source);
            
            this.session_IMAP = session_IMAP;
            
            Message[] messages = new  Message[1];
            messages[0] = message_IMAP;
            return messages;
        } else { // Mail Server Connection properties
            Properties props = getPropertiesIMAP(protocol, 993); // System.getProperties();
            Session session_IMAP = Session.getDefaultInstance(props, null);
            session_IMAP.setDebug(true);
            
            this.session_IMAP = session_IMAP;
            
            Store store = session_IMAP.getStore(protocol.toLowerCase());
            store.connect(server, userName, password);
            Folder inboxFolder = store.getFolder( mailBoxFolder );
            inboxFolder.open(Folder.READ_WRITE);
            Message[] messages = inboxFolder.getMessages();
            if (store.isConnected()) {
                System.out.println("*******************imap store connected*****************");
            }
            return messages;
        }
    }
    
    public void setCertificates(boolean isClassPath, String security_privatekey, String passwordCert, String security_certificate, String protocol) {
        try {
            InputStream privateStream = getCerFileStream(isClassPath, security_privatekey);
            InputStream certStream = getCerFileStream(isClassPath, security_certificate);
        
            if (protocol.equalsIgnoreCase("IMAP") || protocol.equalsIgnoreCase("IMAPS")) {
                setCets_IMAP(privateStream, passwordCert, certStream);
            } else {
                setCets(privateStream, passwordCert, certStream);
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) throws Exception {
        boolean isEncrypt = true, isSign = false;
        SMIMEMial_Util classObj = new SMIMEMial_Util(isSign, isEncrypt, true);
        classObj.setCertificates(true, security_privatekey_Sign, passwordCert_Sign, security_certificate_Encrypt, "SFTP");
        SmimeKey mimeKey = new SmimeKey(classObj.recipientPrivateKey, classObj.cert);
        
        // ===== --- SMTP --- =====
        String fromAddr = "[email protected]",
                toAddr = "[email protected];[email protected]",
                host = "smtpmail.domain.onmicrosoft.com", portSMTP = "25";
        classObj.setProperties(host, portSMTP, false, null, null, false, null);
        Properties props_SMTP = classObj.getPropertiesSFTP();
        Session session_SMTP = Session.getInstance(props_SMTP);
        String mailSubject = "Test Mail SMIME";
        String bodyText = "Sample String Body Content", bodyType = "text/plain; charset=utf-8";
        List<AttachmentFilesSMTP> sampleAttachementData = classObj.getSampleAttachementData();
        classObj.mailSendSFTP(mimeKey, fromAddr, toAddr, mailSubject, session_SMTP, sampleAttachementData, bodyText, bodyType);
        
        // ===== --- IMAP --- =====
        SMIMEMial_Util classObj_IMAP = new SMIMEMial_Util(isSign, isEncrypt, true);
        classObj_IMAP.setCertificates(true, security_privatekey_DEcrypt, passwordCert_Decrypt, security_certificate_Encrypt, "IMAP");
        SmimeKey mimeKey_IMAP = new SmimeKey(classObj_IMAP.recipientPrivateKey, classObj_IMAP.cert);
        
        String protocol = "imap", server = "outlook.office365.com", userName = "[email protected]", password = "12345", mailBoxFolder = "Mail Test";
        int portIMAP = 993, readCount = 2;
        
        Message[] messages = classObj_IMAP.getMessages(classObj_IMAP.isStoreMessage, protocol, server, portIMAP, userName, password, mailBoxFolder, readCount);
        
        List<MailParts> partsInfo = new ArrayList<MailParts>();
        
        for (int i = 0; i < messages.length; i++) {
            MailParts partsObj = new MailParts();
            
            MimeMessage message_IMAP = (MimeMessage) messages[i];
            log.info("MSG: "+ message_IMAP);
            MailParts objPartInfo = classObj_IMAP.readMailIMAP(mimeKey_IMAP, classObj_IMAP.session_IMAP, message_IMAP, partsObj);
            
            partsInfo.add(objPartInfo);
            log.info("Obj :"+objPartInfo);
        }
        System.out.println("partsInfo: "+partsInfo);
    }
    
    MailParts objParts;
    public void setMailObj(MailParts obj) {
        this.objParts = obj;
    }
    public MailParts readMailIMAP(SmimeKey mimeKey, Session session_IMAP, MimeMessage message_IMAP, MailParts partsObj) {
        log.info(" ===== --- IMAP --- ===== ");
        
        try {
            setMailObj(partsObj);
            
            fillMailDetails(message_IMAP);
            getMailParts(session_IMAP, message_IMAP, mimeKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return objParts;
    }
    
    public List<AttachmentFilesSMTP> getSampleAttachementData() throws IOException {
        List<AttachmentFilesSMTP> attachments = new ArrayList<AttachmentFilesSMTP>();
        
        String fileName = "MailSampleAttachement_"+reportDate+".csv";
        if (fileName != null) {
            AttachmentFilesSMTP att = new AttachmentFilesSMTP();
            att.setFileName(fileName);
            att.setContentType("text/csv");
            
            ByteArrayOutputStream outStream = att.getOutStream();
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outStream);
            for (int i = 0; i < 5; i++) {
                bufferedOutputStream.write("CSV DATA\n".getBytes());
            } bufferedOutputStream.flush();
            
            attachments.add(att);
        }
        return attachments;
    }
    public void mailSendSFTP(SmimeKey mimeKey, String fromAddr, String toAddr, String mailSubject, Session session_SMTP,
            List<AttachmentFilesSMTP> attachments, String bodyText, String bodyType) {
        log.info(" ===== --- SMTP --- ===== Session:"+session_SMTP);
        
        try {
        
        MimeMessage message_SMTP = new MimeMessage(session_SMTP);
        log.info("message_SMTP:"+message_SMTP);
        message_SMTP.setFrom(new InternetAddress(fromAddr));
        
        if (toAddr.contains(";")) {
            String[] toAddressList = toAddr.split(";");
            InternetAddress[] mailAddress_TO = new InternetAddress[ toAddressList.length ];
            for (int i = 0; i < toAddressList.length; i++) {
                mailAddress_TO[i] = new InternetAddress( toAddressList[i] );
            }
            message_SMTP.setRecipients(RecipientType.TO, mailAddress_TO);
        } else {
            message_SMTP.setRecipient(RecipientType.TO, new InternetAddress(toAddr));
        }
        message_SMTP.setSubject(mailSubject);
        message_SMTP.setSentDate(new Date());
        
        if (attachments != null && attachments.size() > 0) {
            Multipart multipartContent = new MimeMultipart();
            
            for (Iterator<AttachmentFilesSMTP> it = attachments.iterator(); it.hasNext();) {
                AttachmentFilesSMTP attachment = (AttachmentFilesSMTP) it.next();
                
                InputStream is = attachment.getContent();
                DataSource attachementDataSource = new ByteArrayDataSource(is, attachment.getContentType(), attachment.getFileName());
                
                MimeBodyPart attachmentMimeBody = new MimeBodyPart();
                attachmentMimeBody.setDataHandler(new DataHandler(attachementDataSource));
                attachmentMimeBody.setFileName(attachementDataSource.getName());
                
                multipartContent.addBodyPart(attachmentMimeBody);
            }
            
            if (bodyText != null) {
                MimeBodyPart textBodyPart = new MimeBodyPart();
                textBodyPart.setContent(bodyText, bodyType);
                
                // Add inline image attachments : https://www.codejava.net/java-ee/javamail/embedding-images-into-e-mail-with-javamail
                multipartContent.addBodyPart(textBodyPart);
            }
            message_SMTP.setContent(multipartContent);
        } else {
            message_SMTP.setContent(bodyText, bodyType);
        }
        log.info("message_SMTP setContent competed");
        if (isSigned) {
            SmimeKey mimeKeySign = new SmimeKey(mimeKey.getPrivateKey(), this.recipientPrivateKeyCert);
            MimeMessage signMessage = signMessage(session_SMTP, message_SMTP, fromAddr, mimeKeySign);
            message_SMTP = signMessage;
        }
        
        if (isEncrypted) {
            MimeMessage encryptedSignedMessage = encryptMessage(session_SMTP, message_SMTP, toAddr, mimeKey);
            message_SMTP = encryptedSignedMessage;
        }
        
        if (isStoreMessage) { // Save Message to File
            outputMailFile = outputMailFile.replace("<CurrDate>", reportDate);
            message_SMTP.writeTo(System.out);
            message_SMTP.writeTo(new FileOutputStream(outputMailFile));
            emlReadFile = outputMailFile;
        } else { // Send Mail over Network
            SMTPTransport transport = (SMTPTransport) session_SMTP.getTransport("smtp");
            try {
                if (isSmtpAuth) { // "mail.smtp.port", "25"
                    transport.connect(sftpHost, Integer.valueOf(sftpPort), smtpAuthLogin, smtpAuthPassword);
                } else {
                    transport.connect();
                }

                transport.sendMessage(message_SMTP, message_SMTP.getAllRecipients());
            } finally {
                transport.close();
            }
        }
        
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void fillMailDetails(MimeMessage message_IMAP) throws MessagingException {
        Address[] from = message_IMAP.getFrom();
        objParts.setForm( getMailAddress(from));
        Address[] replyTo = message_IMAP.getAllRecipients();
        objParts.setTo( getMailAddress(replyTo));
        objParts.setSubject(message_IMAP.getSubject());
    }
    public static String getMailAddress(Address[] mailIds) {
        StringBuffer buff = new StringBuffer();
        for (int i = 0; i < mailIds.length; i++) {
            buff.append(mailIds[i]);
            if ( (i + 1) < mailIds.length) buff.append(";");
        }
        return buff.toString();
    }
    public void getMailParts(Session session_IMAP, MimeMessage message_IMAP, SmimeKey mimeKey) throws IOException, MessagingException {
        Object content = message_IMAP.getContent();
        
        if (content instanceof MimeMultipart) { // Signed Part comes under Multipart/mixed.
            log.info("===== MimeMultipart Content -----  MailParts");
            
            MimeMultipart multipart = (MimeMultipart) message_IMAP.getContent();
            fillMultiparts(multipart);
        } else if (content instanceof String) {
            log.info("===== String Content");
            
            String bodyString = (String) content;
            objParts.setBodyContent( bodyString.length() > 4000 ? bodyString.substring(0, 4000) : bodyString );
        } else { // Encrypted Mail part
            // String mimeType = message_IMAP.getHeader("Content-type", ";");
            String contentType = displayHeadersGetContentType(message_IMAP);
            log.info("===== S/MIME ContentType :"+contentType);
            
            SmimeState smimeState = net.markenwerk.utils.mail.smime.SmimeUtil.getStatus(message_IMAP);
            log.info("===== S/MIME SmimeUtil ContentType ::"+smimeState);
            
            if ( contentType.contains("enveloped-data") || (smimeState == SmimeState.ENCRYPTED) ) {
                objParts.setEncrypted(true);
                
                SmimeKey mimeKeyDecrypt = new SmimeKey(mimeKey.getPrivateKey(), this.recipientPrivateKeyCert);
                MimeMessage decryptedMessage = SmimeUtil.decrypt(session_IMAP, message_IMAP, mimeKeyDecrypt);
                log.info("decryptedMessage:"+decryptedMessage);
                
                getMailParts(session_IMAP, decryptedMessage, mimeKey);
            }
            /*if (smimeState == SmimeState.SIGNED) { // When mail only Signed
                obj.setSigned(true);
                boolean validSignature = SmimeUtil.checkSignature(message_IMAP);
                log.info("Mail Entire Signature Status : "+ validSignature);
                obj.setSignatureValid(validSignature);
                
                MimeBodyPart signedContent = SmimeUtil.getSignedContent(message_IMAP);
                //fillMultiparts( (MimeMultipart) signedContent);
            }*/
        }
    }
    public void fillBodyParts(BodyPart bp) throws MessagingException, IOException {
        String contentType = bp.getContentType();
        log.info("===== BodyPart ContentType :"+contentType);
        
        String attachmentName = bp.getFileName();
        InputStream attachmentDataStream = bp.getInputStream();
        
        if (attachmentName != null && attachmentName.length() > 0) {
            objParts.addPart(attachmentName, attachmentDataStream);
        } else {
            BufferedReader reader = new BufferedReader(new InputStreamReader(attachmentDataStream, "UTF-8"));
            StringBuffer sb = new StringBuffer();
            while (reader.ready()) {
                sb.append(reader.readLine());
                sb.append(System.getProperty("line.separator"));
            }
            String bodyString = sb.toString();
            objParts.setBodyContent( bodyString.length() > 4000 ? bodyString.substring(0, 4000) : bodyString );
        }
    }
    public void fillMultiparts(MimeMultipart multipart) throws IOException, MessagingException {
        
        int countParts = multipart.getCount();
        objParts.setMimeMultipart(true);
        String multipartCount = objParts.getMimeMultipartCount();
        if (multipartCount == "") {
            objParts.appendMimeMultipartCount("Parts: "+countParts);
        } else {
            objParts.appendMimeMultipartCount("~ Parts: "+countParts);
        }
        
        for (int j = 0; j < countParts; j++) {
            BodyPart bp = multipart.getBodyPart(j);
            
            String contentType = bp.getContentType();
            log.info("===== Multipart ContentType :"+contentType);
            log.info("BodyPart LineCount "+ bp.getLineCount());
            if (contentType.contains("smime-type=signed-data")) {
                
                SmimeState smimeState = net.markenwerk.utils.mail.smime.SmimeUtil.getStatus(multipart);
                log.info("===== S/MIME Multipart ContentType ::"+smimeState);
                if (smimeState == SmimeState.SIGNED) { // When mail only Signed
                    objParts.setSigned(true);
                    boolean validSignature = SmimeUtil.checkSignature(multipart);
                    log.info("Signature Status : "+ validSignature);
                    objParts.setSignatureValid(validSignature);
                }
                
                String attachmentName = bp.getFileName();
                InputStream attachmentDataStream = bp.getInputStream();
                
                if (attachmentName != null && attachmentName.length() > 0) {
                    objParts.addPart(attachmentName, attachmentDataStream);
                }
            } else if (contentType.contains("multipart/mixed;")) {
                
                Object content = bp.getContent();
                if (content instanceof MimeMultipart) {
                    log.info("===== BodyPart MimeMultipart Content");
                    
                    MimeMultipart multipartBody = (MimeMultipart) content;
                    fillMultiparts(multipartBody);
                } else if (content instanceof String) {
                    log.info("===== BodyPart String Content");
                    
                    String bodyString = (String) content;
                    objParts.setBodyContent( bodyString.length() > 4000 ? bodyString.substring(0, 4000) : bodyString );
                }
            } else {
                fillBodyParts(bp);
            }
        }
    }
    private MimeMessage signMessage(Session session, MimeMessage message, String from, SmimeKey smimeKey) throws Exception {
        return SmimeUtil.sign(session, message, smimeKey);
    }
    private MimeMessage encryptMessage(Session session, MimeMessage message, String to, SmimeKey smimeKey) throws Exception {
        return SmimeUtil.encrypt(session, message, smimeKey.getCertificate());
    }
    public String displayHeadersGetContentType(MimeMessage message) throws MessagingException {
        String contentType = "";
        Enumeration<Header> enumer = message.getAllHeaders();
        log.info("===== Headers Start");
        while (enumer.hasMoreElements()) {
            Header header = (Header) enumer.nextElement();
            System.out.printf("%s ~:~ %s%n", header.getName(), header.getValue());
            
            if (header.getName().equalsIgnoreCase("Content-Type")) {
                contentType = header.getValue();
                log.info("ContentType : "+contentType);
            }
        }
        log.info("===== Headers End");
        return contentType;
    }
    
    private PrivateKey getPrivateCertificate(InputStream privateKeyStream, String passwordCert) {
        PrivateKey recipientPrivateKey = null;
        try {
            KeyStore ks = KeyStore.getInstance("PKCS12");
        
            ks.load(privateKeyStream, passwordCert.toCharArray());
            Enumeration<String> aliases = ks.aliases();
            String alias = null;
            while (aliases.hasMoreElements()) {
                alias = aliases.nextElement(); // Alias: Baeldung
                log.info("Certificate Alias: "+ alias);
            }
            recipientPrivateKey = (PrivateKey) ks.getKey(alias, passwordCert.toCharArray());
                X509Certificate recipientCert = (X509Certificate) ks.getCertificate(alias);
                this.recipientPrivateKeyCert = recipientCert;
            return recipientPrivateKey;
        } catch (KeyStoreException e) {
            log.error(e, e);
        } catch (NoSuchAlgorithmException e) {
            log.error(e, e);
        } catch (CertificateException e) {
            log.error(e, e);
        } catch (IOException e) {
            log.error(e, e);
        } catch (UnrecoverableKeyException e) {
            log.error(e, e);
        }
        return recipientPrivateKey;
    }
    private X509Certificate getX509Certificate(InputStream inputStream) {
        X509Certificate x509Certificate = null;
        CertificateFactory certificateFactory = null;
        try {
            certificateFactory = CertificateFactory.getInstance("X.509", "BC");
            x509Certificate = (X509Certificate) certificateFactory.generateCertificate(inputStream);
        } catch (CertificateException e) {
            log.error(e, e);
        } catch (NoSuchProviderException e) {
            log.error(e, e);
        }
        return x509Certificate;
    }
    static {
        dateFormat = new SimpleDateFormat(DATEPATTERN_STR);
        TimeZone cetTime = TimeZone.getTimeZone(timeZone);
        dateFormat.setTimeZone(cetTime);
        reportDate = dateFormat.format(new Date());
        
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    }
    public InputStream getCerFileStream(boolean isClassPath, String fileName) throws FileNotFoundException {
        InputStream stream = null;
        if (isClassPath) {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            stream = classLoader.getResourceAsStream(fileName);
        } else {
            stream = new FileInputStream(fileName);
        }
        return stream;
    }
}

class AttachmentFilesSMTP {
    private String fileName, contentType;
    private ByteArrayOutputStream outStream = new ByteArrayOutputStream();
    
    public byte[] getByteArray() {
        return outStream.toByteArray();
    }
    public InputStream getContent() throws Exception {
        return new ByteArrayInputStream(getByteArray());
    }
    
    public String getFileName() {
        return fileName;
    }
    public void setFileName(String fileName) {
        this.fileName = fileName;
    }
    public String getContentType() {
        return contentType;
    }
    public void setContentType(String contentType) {
        this.contentType = contentType;
    }
    public ByteArrayOutputStream getOutStream() {
        return outStream;
    }
    public void setOutStream(ByteArrayOutputStream outStream) {
        this.outStream = outStream;
    }
}

log4j.properties

# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=DEBUG, RFile, console

log4j.logger.com.demo.package=debug,console
log4j.additivity.com.demo.package=false

log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.out
log4j.appender.console.immediateFlush=true
log4j.appender.console.encoding=UTF-8
log4j.appender.console.threshold=info
  
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.conversionPattern=%d [%t] %-5p %c - %m%n

# File based log output
log4j.appender.RFile.File=${catalina.home}/logs/MyAppDashboard.log
log4j.appender.RFile=org.apache.log4j.RollingFileAppender
log4j.appender.RFile.MaxFileSize=10000KB
# Keep 80 backup files
log4j.appender.RFile.MaxBackupIndex=80
log4j.appender.RFile.layout=org.apache.log4j.PatternLayout
log4j.appender.RFile.layout.ConversionPattern=   %5p\t[%d] [%t] (%F:%L) %c \n     \t%m%n\n
⚠️ **GitHub.com Fallback** ⚠️