Alfresco 4 Customize inbound email handler

Posted on Thursday, November 1, 2012



This document shows how to override the default Alfresco email handler.   This document assumes you already have Alfresco 4.0 Community Edition installed you have inbound email already configured.

If you do not have this done you can look at my other guide Alfresco_4_Install_on_ubuntu_12_04_and_make_folder_emailable

For this particular install I am using Alfresco 4.0.e on Ubuntu 12.04


Why?


Why would you want to override the default inbound email handler? 

Well, I am sure you can think of many reasons, for me I had a project for a company that would forward emails from gmail and send them all to the same folder.  The custom email handler would check for an attachment and based on its name save it to a different folder.   Also it only saved the attached file and not the email itself.





Tomcat alfresco settings


My install of tomcat is located at /opt/tomcat

The current inbound e-mail settings can be found at

/opt/tomcat/webapps/alfresco/WEB-INF/classes/alfresco/subsystems/email/InboundSMTP

In the file inboundSMTP-context.xml

According to http://wiki.alfresco.com/wiki/Inbound_SMTP_Email_Server_Configuration#Configuration [2] you should be able to copy this folder over to / opt/tomcat/shared/classes/alfresco/extension/subsystems/email/InboundSMTP/inboundSMTP-context.xml   and have it override the default alfresco XML file,  but I can never get this to work for some reason.  I merely copied it over, but it never overrides, if anyone has any advice on this I would love to hear it.

So since I can’t override I just update the default one

Update the default file


       > sudo vi +135 /opt/tomcat/webapps/alfresco/WEB-INF/classes/alfresco/subsystems/email/InboundSMTP/inboundSMTP-context.xml





Edit this

<bean id="folderEmailMessageHandler"
        parent="emailMessageHandlerBase"
        class="org.alfresco.email.server.handler.FolderEmailMessageHandler" >

To this

<bean id="folderEmailMessageHandler"
        parent="emailMessageHandlerBase"
        class="com._10x13.alfresco.email.CustomFolderEmailMessageHandler" >






Create your custom class and install it


Now that the default inbound email handler is overridden a new class needs to be created to replace it.   In the prior section we are looking for a class named com.10x13.alfresco.email.CustomFolderEmailMessageHandler .  Therefore we need to create a program with this same name.

Now before I go too far I want to say this example overrides folderEmailMessageHandler  which only overrides the inbound email handler for folders.   There are other handlers for other objects in alfresco.


Before we get into programming it’s probably a good idea to update the logger so we can log some messages.  To do this you need to edit /opt/tomcat/webapps/alfresco/WEB-INF/classes/log4j.properties



       >  sudo vi /opt/tomcat/webapps/alfresco/WEB-INF/classes/log4j.properties



I added this to the end of the file


##Custom Logger
log4j.logger.com._10x13=INFO, CUSTOM, CUSTOMSTDOUT
log4j.appender.CUSTOM=org.apache.log4j.RollingFileAppender
log4j.appender.CUSTOM.File=/opt/tomcat/logs/emailhandler.log
log4j.appender.CUSTOM.MaxFileSize=50MB
# Keep ten backup file
log4j.appender.CUSTOM.MaxBackupIndex=10

log4j.appender.CUSTOM.layout=org.apache.log4j.PatternLayout
log4j.appender.CUSTOM.layout.ConversionPattern=[%p]::[%d]::"%m":: %C %n

log4j.appender.CUSTOMSTDOUT=org.apache.log4j.ConsoleAppender

log4j.appender.CUSTOMSTDOUT.DatePattern='.'yyyy-MM-dd
log4j.appender.CUSTOMSTDOUT.layout=org.apache.log4j.PatternLayout
log4j.appender.CUSTOMSTDOUT.layout.ConversionPattern=[%p]::[%d]::"%m":: %C %n



Now for the program! I am just going to use command line javadoc and jar to create the program, feel free to use your IDE of choice to do this in a more appropriate way.

To better understand the default Email hander you can check out its javadoc at http://dev.alfresco.com/resource/docs/java/repository/org/alfresco/email/server/handler/FolderEmailMessageHandler.html [1]





First a simple program


First make a simple program that will overwrite the default folder email program and just output a log message.

Run the following command


       >  cd
       >  mkdir MyProgram
       >  cd MyProgram
       >  mkdir dist
       >  mkdir –p com/_10x13/alfresco/email
       >  vi com/_10x13/alfresco/email/CustomFolderEmailMessageHandler.java




Here is the program


package com._10x13.alfresco.email;

import org.alfresco.email.server.handler.FolderEmailMessageHandler;
import org.apache.log4j.Logger;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.email.EmailMessage;

public class CustomFolderEmailMessageHandler extends FolderEmailMessageHandler {
    private Logger log = Logger.getLogger("com._10x13");


    public void processMessage(NodeRef nodeRef, EmailMessage message){

        log.info("This is a test of my Email override Tool");
        super.processMessage(nodeRef, message);
    }
}


To compile to file we need to include several jar files these jar files are

alfresco-data-model-4.0.e.jar
alfresco-repository-4.0.e.jar
log4j-1.2.15.jar


alfresco-data-model-4.0.e.jar is located at
/opt/tomcat/webapps/alfresco/WEB-INF/lib/alfresco-data-model-4.0.e.jar


alfresco-repository-4.0.e.jar is located at 
/opt/tomcat/webapps/alfresco/WEB-INF/lib/alfresco-repository-4.0.e.jar


log4j-1.2.15.jar is located at 
/opt/tomcat/webapps/alfresco/WEB-INF/lib/log4j-1.2.15.jar


To compile your program via the command line and include these jar files run this command


       >  javac -d dist -cp /opt/tomcat/webapps/alfresco/WEB-INF/lib/alfresco-repository-4.0.e.jar:/opt/tomcat/webapps/alfresco/WEB-INF/lib/log4j-1.2.15.jar:/opt/tomcat/webapps/alfresco/WEB-INF/lib/alfresco-data-model-4.0.e.jar ./com/_10x13/alfresco/email/CustomFolderEmailMessageHandler.java



This will place the .class file in the dist directory. 
Now create the jar file


       >  jar cfv custom-email.jar -C dist/ .




copy the jar file to tomcat



       >  sudo cp custom-email.jar /opt/tomcat/webapps/alfresco/WEB-INF/lib/




restart tomcat


       >  sudo /etc/init.d/tomcat stop
       >  sudo /etc/init.d/tomcat start




After it reboots send an email, but first tail the email log


       >  sudo tail -f /opt/tomcat/logs/emailhandler.log



now send an email into your system  (of course this will only work if you had your alfresco set up to accept inbound emails before starting this guide, this guide does not cover that)   in my case I set it up to accept emails to inbox.   So since this is an Amazon server I set an email to inbox@ec2-23-11-12.compute-1.amazonaws.com




Now if you look at the folder you emailed into  (in my case its in share)

The e-mail is there.

























Then look at the log you tailed and you should see





Now that is all well and good but all we really accomplished was to write a log message out.   Let’s do something real.

Here is the code updated to do something interesting



package com._10x13.alfresco.email;

import java.io.InputStream;
import org.alfresco.email.server.handler.FolderEmailMessageHandler;
import org.apache.log4j.Logger;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.email.EmailMessage;
import org.alfresco.service.cmr.email.EmailMessagePart;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;

public class CustomFolderEmailMessageHandler
                                            extends FolderEmailMessageHandler {
    private Logger log = Logger.getLogger("com._10x13");


    public void processMessage(NodeRef folderNodeRef, EmailMessage message){
        String toAddress = message.getTo();
        String fromAddress = message.getFrom();
        String subject = message.getSubject();
        MimetypeService mimetypeService = getMimetypeService();
        InputStream contentIs;
        NodeRef contentNodeRef;
        String mimeType;

        log.info("An Email was sent to: " + toAddress);
        log.info("An Email was sent from: " + fromAddress);
        log.info("An Email was sent with the subject line of: " + subject);


        for(EmailMessagePart attachment: message.getAttachments()) {
            //Skip saving the email as an html file
            if(!attachment.getFileName().startsWith(subject)){
                log.info("attached file is named: " + attachment.getFileName()
                           + "And Has a size of: " + attachment.getSize() + " Bytes");

                contentIs = attachment.getContent();
                mimeType = mimetypeService.guessMimetype(attachment.getFileName());

                contentNodeRef = addContentNode(getNodeService(),
                                                folderNodeRef, attachment.getFileName(), true);

                writeContent(contentNodeRef, contentIs, mimeType,
                                                                    attachment.getEncoding());
            }
        }
    }
}


A lot of new imports on this one  here is a line by line on the important ones and where to find their javadocs

import org.alfresco.service.cmr.repository.NodeRef;

import org.alfresco.service.cmr.email.EmailMessage;
  
import org.alfresco.service.cmr.email.EmailMessagePart;

import org.alfresco.service.cmr.repository.MimetypeService;

import org.alfresco.service.cmr.repository.NodeRef;

import org.alfresco.service.namespace.QName;


Now for some details,  first you will notice that processMessage no longer calls its super class, leaving this class with the sole responsibility to handle the incoming email to a folder.



    public void processMessage(NodeRef folderNodeRef, EmailMessage message){
        String toAddress = message.getTo();
        String fromAddress = message.getFrom();
        String subject = message.getSubject();



In this part of the code you can see how you can get the String for where the email was sent to “message.getTo()”   who the email was sent from “message.getFrom()” and the subject line “message.getSubject()”.




        log.info("An Email was sent to: " + toAddress);
        log.info("An Email was sent from: " + fromAddress);
        log.info("An Email was sent with the subject line of: " + subject);


This is just the log messages




        for(EmailMessagePart attachment: message.getAttachments()) {
            //Skip saving the email as an html file
            if(!attachment.getFileName().startsWith(subject)){


An email can contain several attachments so this is here to iterate through each one.   The if statement is for a special case.   Alfresco considers the body of the email to be a file, well it creates an html version of it.   It names it after the subject line plus a few characters the .html.    This may not be the best way to skip uploading this object but it works well enough for a demonstration.




                log.info("attached file is named: " + attachment.getFileName()
                           + "And Has a size of: " + attachment.getSize() + " Bytes");


Another log message that gives the object name and size in bytes




                contentIs = attachment.getContent();
                mimeType = mimetypeService.guessMimetype(attachment.getFileName());

                contentNodeRef = addContentNode(getNodeService(),
                                                folderNodeRef, attachment.getFileName(), true);

                writeContent(contentNodeRef, contentIs, mimeType,
                                                                    attachment.getEncoding());


contentIs = attachment.getContent(); gets the java.io.InputStream of the attachment.

mimeType = mimetypeService.guessMimetype(attachment.getFileName());
This guesses the mimetype based on the name. There is another one that guesses the mimetype based on the name and does a quick search on the file, but that is more processing time.


contentNodeRef = addContentNode(getNodeService(),
                                                folderNodeRef, attachment.getFileName(), true);

If my memory serves me right on this one, it create a stub file to write to in the correct location.  folderNodeRef must be the parent director you want to place this file into.   The Boolean on the end, if set to true, will overwrite any file with the same name, if you set it to false it will change its name so it can still be uploaded.

If I remember right I remember this being an issue with action folders.  If a folder will perform an action when a new file is created within it then this addContentNode will cause that action to happen even though we have yet to write to it, just a word of warning… that is if I remember correctly.



writeContent(contentNodeRef, contentIs, mimeType,
                                                                    attachment.getEncoding());

This writes the content out. :) 


Now recompile the code, add the new jar file and restart tomcat.


       >  javac -d dist -cp /opt/tomcat/webapps/alfresco/WEB-INF/lib/alfresco-repository-4.0.e.jar:/opt/tomcat/webapps/alfresco/WEB-INF/lib/log4j-1.2.15.jar:/opt/tomcat/webapps/alfresco/WEB-INF/lib/alfresco-data-model-4.0.e.jar ./com/_10x13/alfresco/email/CustomFolderEmailMessageHandler.java



This will place the .class file in the dist directory. 
Now create the jar file


       >  jar cfv custom-email.jar -C dist/ .




copy the jar file to tomcat


       >  sudo cp custom-email.jar /opt/tomcat/webapps/alfresco/WEB-INF/lib/




restart tomcat


       >  sudo /etc/init.d/tomcat stop
       >  sudo /etc/init.d/tomcat start



Now send a new email in this time with attachments.  In my case I attached two files  
  1. Binding Bird.docx  
  2. signed.png


And here you can see that the folder only contains those attachments and no other fluff.



I hope this guide helps you out.  It was a pain when I had to figure it out.



References
[1]  Javadoc Alfresco Folder email handler
       Visited 10/2012
[2]  Alfresco SMTP Configuration
       Visited 11/2012




3 comments:

  1. Hey Patrick,

    I've been referencing your blog a lot lately, and I thank you for your Alfresco posts!

    I think I've discovered how to extend the inboundSMTP-context.xml without simply overwriting the original. It appears that due to changes around 3.2-3.3 (https://issues.alfresco.com/jira/browse/ALF-4361), the new path for overwriting the inbound and outbound SMTP is now extension/subsystems/email/InboundSMTP/inbound/inboundSMTP-context.xml.

    Just wanted to pipe in and give my 2 cents!

    Thanks,
    Jon

    ReplyDelete
  2. Well written and very easy to follow.
    Thank you, you made my day.

    ReplyDelete
  3. Perfect HowTo. Thanks a lot. It saves me ages. Even works with Alfresco 5.1.

    I have only a little issue.
    My annomymous user catches no mails. ERROR: "The eMail user [mail hidden] does not referece a valid accessible node"

    The user is in group EMAIL_CONTRIBUTORS but no admin user in alfresco.
    I setted up a folder alias. But i do not recive any mail.
    For all other users it work like a charm. Any help?

    ReplyDelete