Emailing to Custom Folders Types in Alfresco

I recently had an issue come across my virtual desk that turned out a little more complex than I originally thought.  Before I begin this was done in Alfresco Enterprise 4.1.2 but I would expect it would work with any version above 4.0 (probably anything above 3.2).

I have a client which is sending emails directly into Alfresco.  Originally the plan was to send the emails into a specific folder within a site and then move the emails to the correct location.  I was asked if they could just email the documents into the correct location. Didn't seem like an issue to me so I just updated the view for my custom folder types to show the DB ID of the node and viola everything should be kosher.  It didn't work out that way; the first email that was sent in almost immediately returned with an error message like the following:
5.3.0 - Other mail system problem 554-"Email message handler was not found for node type 'eha:myCustomFolderType'." (delivery attempts: 0)
As it turns out, Alfresco doesn't map sub types of cm:folder to message handler.  I figured this would just be a simple configuration change, probably some property needed to be set in alfresco-global.properties.

What I found is the Spring configuration file alfresco\subsystems\email\InboundSMTP\inboundSMTP-context.xml in the WEB-INF classes directory of the web application.  This file configures the inbound email subsystem.  In this file there is a bean named emailService that contains the following property:

1:  <property name="emailMessageHandlerMap">  
2:    <map>  
3:      <entry key="cm:folder">  
4:        <ref bean="folderEmailMessageHandler"></ref>  
5:      </entry>  
6:      <entry key="cm:content">  
7:        <ref bean="documentEmailMessageHandler"></ref>  
8:      </entry>  
9:      <entry key="fm:forum">  
10:        <ref bean="forumEmailMessageHandler"></ref>  
11:      </entry>  
12:      <entry key="fm:discussion">  
13:        <ref bean="forumEmailMessageHandler"></ref>  
14:      </entry>  
15:      <entry key="fm:topic">  
16:        <ref bean="topicEmailMessageHandler"></ref>  
17:      </entry>  
18:      <entry key="fm:post">  
19:        <ref bean="topicEmailMessageHandler"></ref>  
20:      </entry>  
21:    </map>  
22:  </property>  

OK so this takes the mystery out of how the mapping works, but this isn't exactly what I would call configurable.

I recommend taking a look at the file yourself but let me give you the overview of what is important for this post.  There are a few beans that we care about:
  • emailServer
    • This is the core Alfresco Subetha email server.
  • emailService
    • This is the bean that defines the Alfresco email service which is used by the email server.
  • folderEmailMessageHandler
    • The default message handler for folders.  This is the message handler we want for our custom types.
Now here is the problem: I can't override the configuration of the emailService because it would have already been included by emailServer.  Even if I could I wouldn't want to because it would be a flagrant violation of the DRY (Don't Repeat Yourself) principle.  Anytime you find yourself copying and pasting you are probably violating DRY, especially if you are copying and pasting with only making minimal changes.

So there had to be another way.  After some quick research I found this on Stack Overflow and I had my solution.  Rather than override the bean with a new definition I would just add my mappings to the existing bean after all the beans were created using a BeanFactoryPostProcessor.  So I started writing my test (you are using TDD with Alfresco aren't you?) and ended up with the following class:

1:  package com.ericamell.alfresco.email;  
2:    
3:  import org.alfresco.email.server.handler.EmailMessageHandler;  
4:  import org.springframework.beans.BeansException;  
5:  import org.springframework.beans.factory.config.BeanFactoryPostProcessor;  
6:  import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;  
7:    
8:  import java.util.Map;  
9:    
10:  public class EmailServiceExtender implements BeanFactoryPostProcessor {  
11:    private Map<String, EmailMessageHandler> emailMessageHandlerMap;  
12:    private Map<String, EmailMessageHandler> valuesToAdd;  
13:    
14:    @Override  
15:    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {  
16:      if (valuesToAdd != null)  
17:        addValuesToEmailMessageHandlerMap();  
18:    }  
19:    
20:    private void addValuesToEmailMessageHandlerMap() {  
21:      for (Map.Entry<String, EmailMessageHandler> entry : valuesToAdd.entrySet())  
22:        getEmailMessageHandlerMap().put(entry.getKey(), entry.getValue());  
23:    }  
24:    
25:    public Map<String, EmailMessageHandler> getEmailMessageHandlerMap() {  
26:      return emailMessageHandlerMap;  
27:    }  
28:    
29:    public void setEmailMessageHandlerMap(Map<String, EmailMessageHandler> emailMessageHandlerMap) {  
30:      this.emailMessageHandlerMap = emailMessageHandlerMap;  
31:    }  
32:    
33:    public Map<String, EmailMessageHandler> getValuesToAdd() {  
34:      return valuesToAdd;  
35:    }  
36:    
37:    public void setValuesToAdd(Map<String, EmailMessageHandler> valuesToAdd) {  
38:      this.valuesToAdd = valuesToAdd;  
39:    }  
40:  }  

Now all that was left was to initialize this bean.  Since inbound SMTP is a subsystem it needs to be configured as part of that subsystem.  In other words my context file needs to be in the alfresco.subsystems.email.InboundSMTP package somewhere in the classpath.  You might be thinking to yourself that it should be in alfresco.extension.subsystems.email.InboundSMTP but that is for property configuration not for bean configuration.

My project is using the Maven all in one archetype so I put my context file in the resources directory of my AMP project.  The file itself looks something like this:

1:  <?xml version="1.0" encoding="UTF-8"?>  
2:  <beans xmlns="http://www.springframework.org/schema/beans"  
3:      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
4:      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">  
5:    
6:    <bean class="com.ericamell.alfresco.email.EmailServiceExtender">  
7:      <property name="emailMessageHandlerMap">  
8:        <bean factory-bean="emailService" factory-method="getEmailMessageHandlerMap" />  
9:      </property>  
10:      <property name="valuesToAdd">  
11:        <map>  
12:          <entry key="fn:foreignNationalFolder" value-ref="folderEmailMessageHandler" />  
13:          <entry key="fn:relationFolder" value-ref="folderEmailMessageHandler" />  
14:          <entry key="fn:caseFolder" value-ref="folderEmailMessageHandler" />  
15:        </map>  
16:      </property>  
17:    </bean>  
18:    
19:  </beans>  

This is pretty straight forward there is a bean for my BeanFactoryPostProcessor that defines the map I am extending as the getEmailMessageHandlerMap property of the emailService bean. and then defines the values to add in a map.  I could easily put more than one value in the map and it would add all of the values.  For that matter I could have overrode the message handler for cm:folder if I wanted to.

Hope that helps you.  Happy coding!

If you have better ideas let me know.  I would love to hear them.

Comments

  1. Hi there,
    i would like to ask a question about inbound mail
    so, in my case i want to get the attachment when user send the email and copy the
    attachment to my folder which i want
    but i don't know how to do that
    do you have any advice for me how to do that?

    Regards

    ReplyDelete
    Replies
    1. Ardo,

      Sorry for the late response but was busy this week :). Depending on your exact requirements the simplest way to move the attachment would be to use rules on the folder the email is coming into. Now with that said why not just have the emails go into the folder you want to begin with. Understand the requirement will allow me to point you in the correct direction. Depending on your requirement you may have to implement your own email message handler. That gets a little messy but it can be done. If you have a custom folder type it would actually make it easier since you could use the instructions in this post to use your custom message handler.

      I hope this helps and if you give me more information I may be able to point you in a more specific direction.

      Eric

      Delete
    2. hi eric sorry for my late reply

      Thank for your information
      anyway actually another way if i didn't get the solutions i will use rules on the folder but for now i want to try java api alfresco is it possible or not to do that
      so here is the requirements

      for ex:
      i have 2 users in alfresco let's say
      user1 and user2
      and when user2 wants to email user1 with attachment files let's say "A_001.doc" and "A_002.doc"
      and this attachement will be moved to folder with name "A"
      but for now what is really my problem is how to copy files to folder that i already specified which was "A" folder based on first character name of files
      do you think is that possbile to do that? since i don't really know how to implement "FileFolderService.java"
      thanks eric

      Delete

Post a Comment

Popular posts from this blog

Bootstrapping Rules to Existing Folders in Alfresco

Test Driven Development with Alfresco - Part 3 Test Doubles

Test Driven Development with Alfresco - Part 1 Introduction to TDD