[ Solved ] Spring bean injection in Custom mail workflow

Options
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
11-30-2012 08:43 AM
Hi all,
i'm experiencing some problems with a custom workflow (in Share) i'm developing on Alfresco Community 4.0.
The workflow is very simple:
1) The user inserts an e-mail address
2) The user selects an attachment
3) The user starts the workflow
This script creates the mail:
In the file outboundSMTP-context.xml i changed the class reference in the bean id="mail", replacing
<bean id="mail" class="org.alfresco.repo.action.executer.MailActionExecuter"
with
<bean id="mail" class="org.alfresco.sample.CustomMailActionExecuter"
I had to add this custom class to manage attachments that are not supported in standard Alfresco.
Now to the point: everything works fine if i access alfresco explorer and then alfresco share to start the workflow.
But…if i don't access the explorer before, accessing directly to share, the workflow doesn't work!
I found that the problem is in the Faces Context that, in the second scenario, is found NULL.
The log proves that:
In another discussion i found this:
How can i do this? How can i find out what beans are involved?
The only thing i know is that the problem (obviously!!!) is the attachment and, precisely, i think that the Faces Context is needed when retrieving nodes from repository.
Hereunder the bean involved (outboundSMTP-context.xml)
and the "CustomMailExecuter.java"
Any suggestions?
Thanks in advance,
Paride
i'm experiencing some problems with a custom workflow (in Share) i'm developing on Alfresco Community 4.0.
The workflow is very simple:
1) The user inserts an e-mail address
2) The user selects an attachment
3) The user starts the workflow
This script creates the mail:
var l = bpm_package.children.length;var mail = actions.create("mail");mail.parameters.to = em_mailToAddress;mail.parameters.subject = "You have a file to view! ";mail.parameters.from = initiator.properties["cm:email"];mail.parameters.text = "Kindly approve the document ";if( l ==1 ){ mail.parameters.node=bpm_package.children[0]; mail.execute(bpm_package.children[0]);}else{ mail.execute(bpm_package);}
In the file outboundSMTP-context.xml i changed the class reference in the bean id="mail", replacing
<bean id="mail" class="org.alfresco.repo.action.executer.MailActionExecuter"
with
<bean id="mail" class="org.alfresco.sample.CustomMailActionExecuter"
I had to add this custom class to manage attachments that are not supported in standard Alfresco.
Now to the point: everything works fine if i access alfresco explorer and then alfresco share to start the workflow.
But…if i don't access the explorer before, accessing directly to share, the workflow doesn't work!
I found that the problem is in the Faces Context that, in the second scenario, is found NULL.
The log proves that:
org.springframework.mail.MailPreparationException: Could not prepare mail; nested exception is java.lang.IllegalArgumentException: FacesContext must not be null at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:367) at org.springframework.mail.javamail.JavaMailSenderImpl.send(JavaMailSenderImpl.java:344) at org.alfresco.sample.CustomMailActionExecuter.executeImpl(CustomMailActionExecuter.java:488) at org.alfresco.repo.action.executer.ActionExecuterAbstractBase.execute(ActionExecuterAbstractBase.java:196) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.alfresco.repo.management.subsystems.SubsystemProxyFactory$1.invoke(SubsystemProxyFactory.java:65) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) at $Proxy249.execute(Unknown Source)
In another discussion i found this:
"The problem here is that you are using beans that reference the JSF context. This is only initialised if you use the Alfresco Explorer client (a JSF app). The correct procedure is to use Spring injection of the bean definitions that you require for your action bean"
How can i do this? How can i find out what beans are involved?
The only thing i know is that the problem (obviously!!!) is the attachment and, precisely, i think that the Faces Context is needed when retrieving nodes from repository.
Hereunder the bean involved (outboundSMTP-context.xml)
<bean id="mail" class="org.alfresco.sample.CustomMailActionExecuter" parent="action-executer"> <!– <bean id="mail" class="org.alfresco.repo.action.executer.MailActionExecuter" –> <property name="mailService"> <ref bean="mailService"></ref> </property> <property name="templateService"> <ref bean="templateService"></ref> </property> <property name="personService"> <ref bean="personService"></ref> </property> <property name="authenticationService"> <ref bean="authenticationService"></ref> </property> <property name="nodeService"> <ref bean="nodeService"></ref> </property> <property name="authorityService"> <ref bean="authorityService"></ref> </property> <property name="serviceRegistry"> <ref bean="ServiceRegistry"></ref> </property> <property name="headerEncoding"> <value>${mail.header}</value> </property> <property name="fromAddress"> <value>${mail.from.default}</value> </property> <property name="repoRemoteUrl"> <value>${repo.remote.url}</value> </property> <property name="sendTestMessage"> <value>${mail.testmessage.send}</value> </property> <property name="testMessageTo"> <value>${mail.testmessage.to}</value> </property> <property name="testMessageSubject"> <value>${mail.testmessage.subject}</value> </property> <property name="testMessageText"> <value>${mail.testmessage.text}</value> </property> </bean>
and the "CustomMailExecuter.java"
package org.alfresco.sample;/* * Copyright (C) 2005-2010 Alfresco Software Limited. * * This file is part of Alfresco * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. */import java.io.File;import java.io.Serializable;import java.util.ArrayList;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Set;import javax.faces.context.FacesContext;import javax.mail.MessagingException;import javax.mail.internet.MimeMessage;import org.alfresco.error.AlfrescoRuntimeException;import org.alfresco.model.ContentModel;import org.alfresco.repo.action.ParameterDefinitionImpl;import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;import org.alfresco.repo.action.executer.MailActionExecuter;import org.alfresco.repo.action.executer.TestModeable;import org.alfresco.repo.template.DateCompareMethod;import org.alfresco.repo.template.HasAspectMethod;import org.alfresco.repo.template.I18NMessageMethod;import org.alfresco.repo.template.TemplateNode;import org.alfresco.service.ServiceRegistry;import org.alfresco.service.cmr.action.Action;import org.alfresco.service.cmr.action.ParameterDefinition;import org.alfresco.service.cmr.dictionary.DataTypeDefinition;import org.alfresco.service.cmr.repository.ContentReader;import org.alfresco.service.cmr.repository.NodeRef;import org.alfresco.service.cmr.repository.NodeService;import org.alfresco.service.cmr.repository.TemplateService;import org.alfresco.service.cmr.security.AuthenticationService;import org.alfresco.service.cmr.security.AuthorityService;import org.alfresco.service.cmr.security.AuthorityType;import org.alfresco.service.cmr.security.PersonService;import org.alfresco.util.TempFileProvider;import org.alfresco.web.bean.repository.Repository;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.apache.commons.validator.EmailValidator;import org.springframework.beans.factory.InitializingBean;import org.springframework.mail.MailException;import org.springframework.mail.javamail.JavaMailSender;import org.springframework.mail.javamail.MimeMessageHelper;import org.springframework.mail.javamail.MimeMessagePreparator;/** * Mail action executor implementation. * * @author Roy Wetherall -editet by Savic Prvoslav Savic.Prvoslav@gmail.com MailActionExecuter */public class CustomMailActionExecuter extends ActionExecuterAbstractBase implements InitializingBean, TestModeable{ private static Log logger = LogFactory.getLog(MailActionExecuter.class); /** * Action executor constants */ public static final String NAME = "mail"; public static final String PARAM_TO = "to"; public static final String PARAM_TO_MANY = "to_many"; public static final String PARAM_SUBJECT = "subject"; public static final String PARAM_TEXT = "text"; public static final String PARAM_FROM = "from"; public static final String PARAM_TEMPLATE = "template"; public static final String PARAM_NODE = "node"; /** * From address */ private static final String FROM_ADDRESS = "alfresco@alfresco.org"; private static final String REPO_REMOTE_URL = "http://localhost:8080/alfresco"; /** * The java mail sender */ private JavaMailSender javaMailSender; /** * The Template service */ private TemplateService templateService; /** * The Person service */ private PersonService personService; /** * The Authentication service */ private AuthenticationService authService; /** * The Node Service */ private NodeService nodeService; /** * The Authority Service */ private AuthorityService authorityService; /** * The Service registry */ private ServiceRegistry serviceRegistry; /** * Mail header encoding scheme */ private String headerEncoding = null; /** * Default from address */ private String fromAddress = null; /** * Default alfresco installation url */ private String repoRemoteUrl = null; private boolean sendTestMessage = false; private String testMessageTo = null; private String testMessageSubject = "Test message"; private String testMessageText = "This is a test message."; /** * Test mode prevents email messages from being sent. It is used when unit testing when we don't actually want to * send out email messages. * * MER 20/11/2009 This is a quick and dirty fix. It should be replaced by being "mocked out" or some other better * way of running the unit tests. */ private boolean testMode = false; private MimeMessage lastTestMessage; /** * @param javaMailSender * the java mail sender */ public void setMailService(JavaMailSender javaMailSender) { this.javaMailSender = javaMailSender; } /** * @param templateService * the TemplateService */ public void setTemplateService(TemplateService templateService) { this.templateService = templateService; } /** * @param personService * the PersonService */ public void setPersonService(PersonService personService) { this.personService = personService; } /** * @param authService * the AuthenticationService */ public void setAuthenticationService(AuthenticationService authService) { this.authService = authService; } /** * @param serviceRegistry * the ServiceRegistry */ public void setServiceRegistry(ServiceRegistry serviceRegistry) { this.serviceRegistry = serviceRegistry; } /** * @param authorityService * the AuthorityService */ public void setAuthorityService(AuthorityService authorityService) { this.authorityService = authorityService; } /** * @param nodeService * the NodeService to set. */ public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } /** * @param headerEncoding * The mail header encoding to set. */ public void setHeaderEncoding(String headerEncoding) { this.headerEncoding = headerEncoding; } /** * @param fromAddress * The default mail address. */ public void setFromAddress(String fromAddress) { this.fromAddress = fromAddress; } /** * * @param repoRemoteUrl * The default alfresco installation url */ public void setRepoRemoteUrl(String repoRemoteUrl) { this.repoRemoteUrl = repoRemoteUrl; } public void setTestMessageTo(String testMessageTo) { this.testMessageTo = testMessageTo; } public void setTestMessageSubject(String testMessageSubject) { this.testMessageSubject = testMessageSubject; } public void setTestMessageText(String testMessageText) { this.testMessageText = testMessageText; } public void setSendTestMessage(boolean sendTestMessage) { this.sendTestMessage = sendTestMessage; } @Override public void init() { super.init(); if (sendTestMessage) { Map<String, Serializable> params = new HashMap<String, Serializable>(); params.put(PARAM_TO, testMessageTo); params.put(PARAM_SUBJECT, testMessageSubject); params.put(PARAM_TEXT, testMessageText); Action ruleAction = serviceRegistry.getActionService().createAction(NAME, params); executeImpl(ruleAction, null); } } /** * Initialise bean */ public void afterPropertiesSet() throws Exception { if (fromAddress == null || fromAddress.length() == 0) { fromAddress = FROM_ADDRESS; } if (repoRemoteUrl == null || repoRemoteUrl.length() == 0) { repoRemoteUrl = REPO_REMOTE_URL; } } /** * Send an email message * * @throws AlfrescoRuntimeExeption */ @Override protected void executeImpl(final Action ruleAction, final NodeRef actionedUponNodeRef) { // Create the mime mail message MimeMessagePreparator mailPreparer = new MimeMessagePreparator() { @SuppressWarnings("unchecked") public void prepare(MimeMessage mimeMessage) throws MessagingException { if (logger.isDebugEnabled()) { logger.debug(ruleAction.getParameterValues()); } MimeMessageHelper message = new MimeMessageHelper(mimeMessage); NodeRef nodeRef = (NodeRef) ruleAction.getParameterValue(PARAM_NODE); if (nodeRef == null) { message = new MimeMessageHelper(mimeMessage); } else { message = new MimeMessageHelper(mimeMessage, true); } if (nodeRef != null) { ContentReader contentReader = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getContentService().getReader(nodeRef, ContentModel.PROP_CONTENT); File file = TempFileProvider.createTempFile("mail", "action"); contentReader.getContent(file); NodeService nodeService = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getNodeService(); if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TITLED)) { String title = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_TITLE); if (title != null && title.trim().equals("")) { message.addAttachment(title, file); } } else { String nodeName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); message.addAttachment(nodeName, file); } message.addAttachment("test", file); } // // set header encoding if one has been supplied if (headerEncoding != null && headerEncoding.length() != 0) { // mimeMessage.setHeader("Content-Transfer-Encoding", headerEncoding); } // set recipient String to = (String) ruleAction.getParameterValue(PARAM_TO); if (to != null && to.length() != 0) { message.setTo(to); } else { // see if multiple recipients have been supplied - as a list of authorities Serializable authoritiesValue = ruleAction.getParameterValue(PARAM_TO_MANY); List<String> authorities = null; if (authoritiesValue != null) { if (authoritiesValue instanceof String) { authorities = new ArrayList<String>(1); authorities.add((String) authoritiesValue); } else { authorities = (List<String>) authoritiesValue; } } if (authorities != null && authorities.size() != 0) { List<String> recipients = new ArrayList<String>(authorities.size()); for (String authority : authorities) { AuthorityType authType = AuthorityType.getAuthorityType(authority); if (authType.equals(AuthorityType.USER)) { if (personService.personExists(authority) == true) { NodeRef person = personService.getPerson(authority); String address = (String) nodeService.getProperty(person, ContentModel.PROP_EMAIL); if (address != null && address.length() != 0 && validateAddress(address)) { recipients.add(address); } } } else if (authType.equals(AuthorityType.GROUP)) { // else notify all members of the group Set<String> users = authorityService.getContainedAuthorities(AuthorityType.USER, authority, false); for (String userAuth : users) { if (personService.personExists(userAuth) == true) { NodeRef person = personService.getPerson(userAuth); String address = (String) nodeService.getProperty(person, ContentModel.PROP_EMAIL); if (address != null && address.length() != 0) { recipients.add(address); } } } } } message.setTo(recipients.toArray(new String[recipients.size()])); } else { // No recipiants have been specified logger.error("No recipiant has been specified for the mail action"); } } // set subject line message.setSubject((String) ruleAction.getParameterValue(PARAM_SUBJECT)); // See if an email template has been specified String text = null; NodeRef templateRef = (NodeRef) ruleAction.getParameterValue(PARAM_TEMPLATE); if (templateRef != null) { // build the email template model Map<String, Object> model = createEmailTemplateModel(actionedUponNodeRef); // process the template against the model text = templateService.processTemplate("freemarker", templateRef.toString(), model); } // set the text body of the message if (text == null) { text = (String) ruleAction.getParameterValue(PARAM_TEXT); } message.setText(text, true); // set the from address NodeRef person = personService.getPerson(authService.getCurrentUserName()); String fromActualUser = null; if (person != null) { fromActualUser = (String) nodeService.getProperty(person, ContentModel.PROP_EMAIL); } if (fromActualUser != null && fromActualUser.length() != 0) { message.setFrom(fromActualUser); } else { String from = (String) ruleAction.getParameterValue(PARAM_FROM); if (from == null || from.length() == 0) { message.setFrom(fromAddress); } else { message.setFrom(from); } } } }; try { // Send the message unless we are in "testMode" if (!testMode) { javaMailSender.send(mailPreparer); } else { try { MimeMessage mimeMessage = javaMailSender.createMimeMessage(); mailPreparer.prepare(mimeMessage); lastTestMessage = mimeMessage; } catch (Exception e) { System.err.println(e); } } } catch (MailException e) { String to = (String) ruleAction.getParameterValue(PARAM_TO); if (to == null) { Object obj = ruleAction.getParameterValue(PARAM_TO_MANY); if (obj != null) { to = obj.toString(); } } logger.error("Failed to send email to " + to, e); throw new AlfrescoRuntimeException("Failed to send email to:" + to, e); } } /** * Return true if address has valid format * * @param address * @return */ private boolean validateAddress(String address) { boolean result = false; EmailValidator emailValidator = EmailValidator.getInstance(); if (emailValidator.isValid(address)) { result = true; } else { logger.error("Failed to send email to '" + address + "' as the address is incorrectly formatted"); } return result; } /** * @param ref * The node representing the current document ref * * @return Model map for email templates */ private Map<String, Object> createEmailTemplateModel(NodeRef ref) { Map<String, Object> model = new HashMap<String, Object>(8, 1.0f); NodeRef person = personService.getPerson(authService.getCurrentUserName()); model.put("person", new TemplateNode(person, serviceRegistry, null)); model.put("document", new TemplateNode(ref, serviceRegistry, null)); NodeRef parent = serviceRegistry.getNodeService().getPrimaryParent(ref).getParentRef(); model.put("space", new TemplateNode(parent, serviceRegistry, null)); // current date/time is useful to have and isn't supplied by FreeMarker by default model.put("date", new Date()); // add custom method objects model.put("hasAspect", new HasAspectMethod()); model.put("message", new I18NMessageMethod()); model.put("dateCompare", new DateCompareMethod()); model.put("url", new URLHelper(repoRemoteUrl)); return model; } /** * Add the parameter definitions */ @Override protected void addParameterDefinitions(List<ParameterDefinition> paramList) { paramList.add(new ParameterDefinitionImpl(PARAM_TO, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_TO))); paramList.add(new ParameterDefinitionImpl(PARAM_TO_MANY, DataTypeDefinition.ANY, false, getParamDisplayLabel(PARAM_TO_MANY), true)); paramList.add(new ParameterDefinitionImpl(PARAM_SUBJECT, DataTypeDefinition.TEXT, true, getParamDisplayLabel(PARAM_SUBJECT))); paramList.add(new ParameterDefinitionImpl(PARAM_TEXT, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_TEXT))); paramList.add(new ParameterDefinitionImpl(PARAM_FROM, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_FROM))); paramList.add(new ParameterDefinitionImpl(PARAM_TEMPLATE, DataTypeDefinition.NODE_REF, false, getParamDisplayLabel(PARAM_TEMPLATE), false, "ac-email-templates")); } public void setTestMode(boolean testMode) { this.testMode = testMode; } public boolean isTestMode() { return testMode; } /** * Returns the most recent message that wasn't sent because TestMode had been enabled. */ public MimeMessage retrieveLastTestMessage() { return lastTestMessage; } public static class URLHelper { String contextPath; String serverPath; public URLHelper(String repoRemoteUrl) { String[] parts = repoRemoteUrl.split("/"); this.contextPath = "/" + parts[parts.length - 1]; this.serverPath = parts[0] + "//" + parts[2]; } public String getContext() { return this.contextPath; } public String getServerPath() { return this.serverPath; } }}
Any suggestions?
Thanks in advance,
Paride
Labels:
- Labels:
-
Archive
1 REPLY 1

Options
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
01-09-2013 11:05 AM
Finally i solved by my self, changing the strategy!
Rather than injecting the bean i needed, i changed the way i was getting the serviceRegistry, without using the FacesContext.
You can found the solution in this very helpful link: http://86anuj.blogspot.it/2010/03/different-approaches-of-accessing.html
Kind regards, Paride!
Rather than injecting the bean i needed, i changed the way i was getting the serviceRegistry, without using the FacesContext.
You can found the solution in this very helpful link: http://86anuj.blogspot.it/2010/03/different-approaches-of-accessing.html
Kind regards, Paride!
