cancel
Showing results for 
Search instead for 
Did you mean: 

Email Custom Processing

jordan
Champ in-the-making
Champ in-the-making
Hi,

We are trying out the custom processing of the Email received over the SMTP. The information on the the page http://wiki.alfresco.com/wiki/Email_Server_Configuration is not very clear in terms of configuration to be done. Can some one please help where and how to configure the following code.
<property name="emailMessageHandlerMap">
      <map>
         <entry key="cm:folder">
            <ref bean="folderEmailMessageHandler"></ref>
         </entry>
         <entry key="cm:content">
            <ref bean="documentEmailMessageHandler"></ref>
         </entry>
      </map>
   </property>

Do we need to replace the existing one or how to go about it. Any pointers?
10 REPLIES 10

jayjayecl
Confirmed Champ
Confirmed Champ
everything got fine for me with sample config.

What goes wrong ? Do you have any stacktrace ?

jordan
Champ in-the-making
Champ in-the-making
The email-service-context.xml already contains some configuration regarding the same. Do I need to replace it or need to define a new configuration?

This is the existing one
<bean id="emailService" class="org.alfresco.email.server.EmailServiceImpl">
                <property name="emailInboundEnabled">
                        <value>${email.inbound.enabled}</value>
                </property>
                <property name="unknownUser">
         <value>${email.inbound.unknownUser}</value>
                </property>
                <property name="emailMessageHandlerMap">
                        <map>
                                <entry key="cm:folder">
                                        <ref bean="folderEmailMessageHandler"></ref>
                                </entry>
                                <entry key="cm:content">
                                        <ref bean="documentEmailMessageHandler"></ref>
                                </entry>
                                <entry key="fm:forum">
                                        <ref bean="forumEmailMessageHandler"></ref>
                                </entry>
                                <entry key="fm:discussion">
                                        <ref bean="forumEmailMessageHandler"></ref>
                                </entry>
                                <entry key="fm:topic">
                                        <ref bean="topicEmailMessageHandler"></ref>
                                </entry>
                                <entry key="fm:post">
                                        <ref bean="topicEmailMessageHandler"></ref>
                                </entry>
                        </map>
                </property>
                <property name="namespaceService">
                        <ref bean="NamespaceService" />
                </property>
                <property name="nodeService">
                        <ref bean="NodeService" />
                </property>
                <property name="searchService">
                        <ref bean="SearchService" />
                </property>
                <property name="retryingTransactionHelper">
                        <ref bean="retryingTransactionHelper" />
                </property>
        </bean>

Here the folder, content, forum, discussion, topic, post are defined as separate beans. Do all these needs to be implemented? Can you please let me know what needs to be implemented and that needs to be configured?

I understand that I need to implement a class with
void processMessage(NodeRef nodeRef, EmailMessage message)
. But not sure how to configure or induce it into the alfresco system.

jayjayecl
Confirmed Champ
Confirmed Champ
Do you need to get this work out, or do you need to customize the mail processing (implementing your own code) ?

If you need to implement your own code, in the hypothetical case of you're sending an email to a content item (cm:content), do this :

Beans :


<bean id="emailService" class="org.alfresco.email.server.EmailServiceImpl">
                <property name="emailInboundEnabled">
                        <value>${email.inbound.enabled}</value>
                </property>
                <property name="unknownUser">
         <value>${email.inbound.unknownUser}</value>
                </property>
                <property name="emailMessageHandlerMap">
                        <map>
                                <entry key="cm:folder">
                                        <ref bean="folderEmailMessageHandler"></ref>
                                </entry>
                                <entry key="cm:content">
                                        <ref bean="customDocumentEmailMessageHandler"></ref>
                                </entry>
                                <entry key="fm:forum">
                                        <ref bean="forumEmailMessageHandler"></ref>
                                </entry>
                                <entry key="fm:discussion">
                                        <ref bean="forumEmailMessageHandler"></ref>
                                </entry>
                                <entry key="fm:topic">
                                        <ref bean="topicEmailMessageHandler"></ref>
                                </entry>
                                <entry key="fm:post">
                                        <ref bean="topicEmailMessageHandler"></ref>
                                </entry>
                        </map>
                </property>
                <property name="namespaceService">
                        <ref bean="NamespaceService" />
                </property>
                <property name="nodeService">
                        <ref bean="NodeService" />
                </property>
                <property name="searchService">
                        <ref bean="SearchService" />
                </property>
                <property name="retryingTransactionHelper">
                        <ref bean="retryingTransactionHelper" />
                </property>
        </bean>

<bean id="customDocumentEmailMessageHandler"
      parent="emailMessageHandlerBase"
      class="your.package.written.here.CustomDocumentEmailMessageHandler" />


Dans build a JavaClass "CustomDocumentEmailMessageHandler" taht would be highly inspired from DocumentEmailMessageHandler, adding your own code in the "processMessage" method

jordan
Champ in-the-making
Champ in-the-making
Dear Jay,

I want to implement my own code there. I want the entire processing to by done by a custom file and not use the existing code at all.

As you mentioned "you're sending an email to a content item (cm:content)" When a email is received I don't know where exactly this content type is looked into or how to set it in the email. This is one of the reasons why I want to implement a entirely new handler . If setting custom type is possible in mail, I think the way you mentioned above will do.

If that is not possible I may need to replace the entire processing.

Please let me know the approach I should take.

jayjayecl
Confirmed Champ
Confirmed Champ
You might then replace the EmailServiceImpl class.

Have a look at the following :

- basically, when an email is sent to the Alfresco mail server, the method processMessage is called.


/**
     * Process the message. Method is called after filtering by sender's address.
     *
     * @param nodeRef Addressed node (target node).
     * @param message Email message
     * @throws EmailMessageException Any exception occured inside the method will be converted and thrown as EmailMessageException
     */
    private void processMessage(final NodeRef nodeRef, final EmailMessage message)
    {
        if (!emailInboundEnabled)
        {
            throw new EmailMessageException(ERR_INBOUND_EMAIL_DISABLED);
        }
        try
        {
            // Get the username for the process using the system account
            final RetryingTransactionCallback<String> getUsernameCallback = new RetryingTransactionCallback<String>()
            {
                public String execute() throws Throwable
                {
                    String from = message.getFrom();
                    return getUsername(from);
                }
            };
            RunAsWork<String> getUsernameRunAsWork = new RunAsWork<String>()
            {
                public String doWork() throws Exception
                {
                    return retryingTransactionHelper.doInTransaction(getUsernameCallback, false);
                }
            };
            String username = AuthenticationUtil.runAs(getUsernameRunAsWork, AuthenticationUtil.SYSTEM_USER_NAME);
           
            // Process the message using the username's account
            final RetryingTransactionCallback<Object> processMessageCallback = new RetryingTransactionCallback<Object>()
            {
                public Object execute() throws Throwable
                {
                    String recipient = message.getTo();
                    NodeRef targetNodeRef = null;
                    if (nodeRef == null)
                    {
                        targetNodeRef = getTargetNode(recipient);
                    }
                    else
                    {
                        targetNodeRef = nodeRef;
                    }
                    EmailMessageHandler messageHandler = getMessageHandler(targetNodeRef);
                    messageHandler.processMessage(targetNodeRef, message);
                    return null;
                }
            };
            RunAsWork<Object> processMessageRunAsWork = new RunAsWork<Object>()
            {
                public Object doWork() throws Exception
                {
                    return retryingTransactionHelper.doInTransaction(processMessageCallback, false);
                }
            };
            AuthenticationUtil.runAs(processMessageRunAsWork, username);
        }
        catch (EmailMessageException e)
        {
            // These are email-specific errors
            throw e;
        }
        catch (AccessDeniedException e)
        {
            throw new EmailMessageException(ERR_ACCESS_DENIED, message.getFrom(), message.getTo());
        }
        catch (IntegrityException e)
        {
            throw new EmailMessageException(ERR_INVALID_SUBJECT);
        }
        catch (Throwable e)
        {
            throw new AlfrescoRuntimeException("Email message processing failed", e);
        }
    }


- the important part is the following :



// if the mail is sent to test-target@alfresco.com, recipient = test-target
String recipient = message.getTo();


NodeRef targetNodeRef = null;
if (nodeRef == null)
{
           // Looking for an object that would have "test-target" as an email-id or email-alias
           targetNodeRef = getTargetNode(recipient);
}
else
{
           targetNodeRef = nodeRef;
}
// getting Message classHandler according to the target object type
EmailMessageHandler messageHandler = getMessageHandler(targetNodeRef);
messageHandler.processMessage(targetNodeRef, message);
return null;

- the getMessageHandler method is important :


private EmailMessageHandler getMessageHandler(NodeRef nodeRef)
    {
        ParameterCheck.mandatory("nodeRef", nodeRef);
       
        QName nodeTypeQName = nodeService.getType(nodeRef);
        String prefixedNodeTypeStr = nodeTypeQName.toPrefixString(namespaceService);
        EmailMessageHandler handler = emailMessageHandlerMap.get(prefixedNodeTypeStr);
        if (handler == null)
        {
            throw new EmailMessageException(ERR_HANDLER_NOT_FOUND, prefixedNodeTypeStr);
        }
        return handler;
    }

The map "emailMessageHandlerMap" is the map defined in the XML spring config file.
Then, you'll guess you can add new Handlers to the map (in the xml), change handlers among the map etc …



As for me, if you replace EmailServiceImpl with your custom class (widely inspired from the existing one), you can do whatever you want.
What don't you understand ?

jordan
Champ in-the-making
Champ in-the-making
If I take this approach I will be changing the Alfresco classes and will need to recompile it. I want to implement a custom process without touching any existing code of the alfresco and by just configuring it to run with a custom class I build. I will have a look once again on the things you have mentioned.

Thanks for your help till now.

Please let me know what is the approach to be taken.

jayjayecl
Confirmed Champ
Confirmed Champ
you did not get what I meant.
Are you comfortable with Spring IoC layer ?

I meant the following : you build your own "CustomEmailServiceImpl" java class, in your own "my.own.custom.package" package.
You don't need to recompile Alfresco.

In the XML config file you found (email-service-context.xml), you tell Alfresco not to run his own class, but yours :


<bean id="emailService" class="my.own.custom.package.CustomEmailServiceImpl">
                <property name="emailInboundEnabled">
                        <value>${email.inbound.enabled}</value>
                </property>
                <property name="unknownUser">
         <value>${email.inbound.unknownUser}</value>
                </property>
                <property name="emailMessageHandlerMap">
                        <map>
                                <entry key="cm:folder">
                                        <ref bean="folderEmailMessageHandler"></ref>
                                </entry>
                                <entry key="cm:content">
                                        <ref bean="documentEmailMessageHandler"></ref>
                                </entry>
                                <entry key="fm:forum">
                                        <ref bean="forumEmailMessageHandler"></ref>
                                </entry>
                                <entry key="fm:discussion">
                                        <ref bean="forumEmailMessageHandler"></ref>
                                </entry>
                                <entry key="fm:topic">
                                        <ref bean="topicEmailMessageHandler"></ref>
                                </entry>
                                <entry key="fm:post">
                                        <ref bean="topicEmailMessageHandler"></ref>
                                </entry>
                        </map>
                </property>
                <property name="namespaceService">
                        <ref bean="NamespaceService" />
                </property>
                <property name="nodeService">
                        <ref bean="NodeService" />
                </property>
                <property name="searchService">
                        <ref bean="SearchService" />
                </property>
                <property name="retryingTransactionHelper">
                        <ref bean="retryingTransactionHelper" />
                </property>
        </bean>
$

Then,

- no you're not changing Alfresco classes
- no you don't need to recompile Alfresco
- no you do not touch the existing code of Alfresco, jsut telling him (by XML config) to run your own code
- yes you're just configuring it to run with a custom class you built

Hope this helps

jordan
Champ in-the-making
Champ in-the-making
Dear Jay,

This is what I have asked in my second post "Do I need to replace it or need to define a new configuration?"
From what  you have mentioned above, it will be like replacing the entire configuration. Fine. So it only works on my own class implementation.

Now I have content type sc:myPhoto.
If I need to process the default types by the existing logic and then process the sc:myPhoto with a different logic, what is that I need to do. Do I need to implement the custom for default classes too.

I believe the change of class in emailService is going to remove the class from the flow even though they are physically present in the system. In this case I may not be able to use them.

The other way is copy paste the entire EmailServiceImpl and add the extra stuff which will be a redundant code on the server.

jayjayecl
Confirmed Champ
Confirmed Champ
Now I have content type sc:myPhoto.
If I need to process the default types by the existing logic and then process the sc:myPhoto with a different logic, what is that I need to do. Do I need to implement the custom for default classes too.

Yes,

In fact, you add a new "type" in the map :

<entry key="sc:myPhoto">
                                        <ref bean="myPhotoEmailMessageHandler"></ref>
                                </entry>

and then, further, declare your "myPhotoEmailMessageHandler" bean :

<bean id="myPhotoEmailMessageHandler"
      parent="emailMessageHandlerBase"
      class="my.own.custom.package.myPhotoEmailMessageHandler" />

Implement your custom code into myPhotoEmailMessageHandler (processMessage(NodeRef nodeRef, EmailMessage message) )

I believe the change of class in emailService is going to remove the class from the flow even though they are physically present in the system. In this case I may not be able to use them.

The other way is copy paste the entire EmailServiceImpl and add the extra stuff which will be a redundant code on the server.

This is what I meant by saying "copy the existing class" or again "widely inspired by the Alfresco class"