Foren

DXP : Sending mail with a web service instead of SMTP

Guillaume Gauthier, geändert vor 6 Jahren.

DXP : Sending mail with a web service instead of SMTP

New Member Beiträge: 7 Beitrittsdatum: 23.06.14 Neueste Beiträge
Hello,

On a Liferay 7 DXP project, I need to send emails by calling a (third party) web service instead of using an SMTP server.

I tried to find a way with a hook but it seems it's not possible because there is no MailServiceWrapper to hook MailService.
As far as I know, there is no ext plugin in Liferay 7 but reading this page, it seems to be possible starting from Liferay GA4. I tryed to generate a ext with "mvn archetype:generate", there is one archetype for Liferay SP3 (7.0.0-m2) if I'm right. It doesn't build, it seems to looking for wrong dependecies so I'm pretty sure to going to the wrong way...

Is there any way to override MailServiceImpl or MailMessageListener ?
Enventually, I was thinking about creating a message listener to get all email messages in the message bus but, the native code will still try to send emails with errors in log if I don't cut it of.

Any help will be appreciated.
Thanks
thumbnail
Andrew Jardine, geändert vor 6 Jahren.

RE: DXP : Sending mail with a web service instead of SMTP

Liferay Legend Beiträge: 2416 Beitrittsdatum: 22.12.10 Neueste Beiträge
Hi Guillaume,

I'm going to throw out a disclaimer here and notify you that I have never actually done this before, but I have an idea if you want to try it out! emoticon

As you mentioned, there is a listener that is registered to send the mail. That is the crux of the issue for you -- how to stop the default implementation from going. Well, first, you can stop those using a brute force method -- simply not providing a valid configuration. No mail settings, no mail. One problem solved.

The next issue is how to get your custom implementation to do it. Well, if I look at the Liferay source I can see where the listener registration happens in the spring config file. It looks like this --

<!--?xml version="1.0"?-->

<beans default-destroy-method="destroy" default-init-method="afterPropertiesSet" xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
	<bean class="com.liferay.mail.service.impl.MailServiceImpl" id="com.liferay.mail.kernel.service.MailService" />

	<!-- Messaging -->

	<bean class="com.liferay.mail.messaging.MailMessageListener" id="messageListener.mail" />
	<bean class="com.liferay.portal.kernel.messaging.config.DefaultMessagingConfigurator" id="messagingConfigurator.mail">
		<property name="destinationConfigurations">
			<util:set>
				<bean class="com.liferay.portal.kernel.messaging.DestinationConfiguration">
					<constructor-arg name="destinationName" value="liferay/mail" />
					<constructor-arg name="destinationType">
						<util:constant static-field="com.liferay.portal.kernel.messaging.DestinationConfiguration.DESTINATION_TYPE_PARALLEL" />
					</constructor-arg>
				</bean>
			</util:set>
		</property>
		<property name="messageListeners">
			<map key-type="java.lang.String" value-type="java.util.List">
				<entry key="liferay/mail">
					<list value-type="com.liferay.portal.kernel.messaging.MessageListener">
						<ref bean="messageListener.mail" />
					</list>
				</entry>
			</map>
		</property>
	</bean>
</beans>


the part in this file that I care about the most is the destination name because it provides me with a place that listeners can be registered. So in this case --

<constructor-arg name="destinationName" value="liferay/mail" />


So, what I am thinking is that you create your own component and register it with this destination. Then when the mail events are routed, the default mail class liferay provides will get them, but so will yours (I think). So maybe something like this --

@Component (
    immediate = true,
    service = CustomMailService.class
)
public class CustomMailService extends BaseMessageListener {

    @Reference(unbind="-")
    private SchedulerEngineHelper _schedulerEngineHelper;

    @Activate
    @Modified
    protected void activate(Map<string, object> properties) {
        Class<!--?--> clazz = getClass();
        String className = clazz.getName();

        ScheduleEntry schedulerEntry = new SchedulerEntryImpl();
        schedulerEntry.setEventListenerClass(className);

        _schedulerEngineHelper.register(this, schedulerEntry, DestinationNames.MAIL);
    }
}</string,>


... something like that anyway. You could even go as far as unregistering the exiting mail service. There is a method on the _schedulerEngineHelper that might let you do it.

If you do try it out, let me know if it works emoticon
Guillaume Gauthier, geändert vor 6 Jahren.

RE: DXP : Sending mail with a web service instead of SMTP

New Member Beiträge: 7 Beitrittsdatum: 23.06.14 Neueste Beiträge
Hi Andrew,

Yes that was the kind of solution I was thinking about but while it need to override core file manually to disable default email component, I was hoping there is another way.
The problem that left is that the default implementation will log error, so I'll take a look to the _schedulerEngineHelper, if it doesn't fit my needs I can still override the spring file to remove the listener declaration or change the destination (only needed at first installation time so, it's not such worst). Or maybe I'll just disable the log but I don't like that either.

I'll give the solution once I make it works.

Thanks for you answer.
thumbnail
Andrew Jardine, geändert vor 6 Jahren.

RE: DXP : Sending mail with a web service instead of SMTP (Antwort)

Liferay Legend Beiträge: 2416 Beitrittsdatum: 22.12.10 Neueste Beiträge
Well, you could try, in the activate handler for the bundle, using the API to figure out if there is already a listener registered for the destination, and if there is, unregister ir? So basically, your module would undo the default and drop in your custom one.
Guillaume Gauthier, geändert vor 6 Jahren.

RE: DXP : Sending mail with a web service instead of SMTP (Antwort)

New Member Beiträge: 7 Beitrittsdatum: 23.06.14 Neueste Beiträge
So below the solution I used:
@Component (
        immediate = true,
        ...
        )
public class SendGridMailListener extends BaseMessageListener {
    @Activate
    @Modified
    protected void activate(Map<string, object> properties) {
        Destination destination = MessageBusUtil.getDestination(DestinationNames.MAIL);
        messageListeners = new ArrayList<messagelistener>(destination.getMessageListeners());
        destination.unregisterMessageListeners();
        destination.register(this);
    }
    @Deactivate
    @Modified
    protected void deactivate(Map<string, object> properties) {
        Destination destination = MessageBusUtil.getDestination(DestinationNames.MAIL);
        destination.unregisterMessageListeners();
        for(MessageListener messageListener : messageListeners) {
            destination.register(messageListener);
        }
    }

    @Override
    protected void doReceive(Message message) {
       //Send mail with sendgrid web service
    }
}
</string,></messagelistener></string,>

Note that some MessageBusUtil.unregisterMessageListerner where not working so the only way is to unregister from the Destination object.
thumbnail
Andrew Jardine, geändert vor 6 Jahren.

RE: DXP : Sending mail with a web service instead of SMTP

Liferay Legend Beiträge: 2416 Beitrittsdatum: 22.12.10 Neueste Beiträge
Other than that though, the solution worked?
Guillaume Gauthier, geändert vor 6 Jahren.

RE: DXP : Sending mail with a web service instead of SMTP

New Member Beiträge: 7 Beitrittsdatum: 23.06.14 Neueste Beiträge
Yes it works, this way my message listener intercepts all MailMessage when the module is deployed.
thumbnail
Vilcapaza javier, geändert vor 5 Jahren.

RE: DXP : Sending mail with a web service instead of SMTP

New Member Beitrag: 1 Beitrittsdatum: 11.09.12 Neueste Beiträge

thank, this works for me:

@Component(immediate = true, service = CustomMailListener.class)
public class CustomMailListener extends BaseMessageListener {

    private ArrayList<MessageListener> messageListeners;
    @Activate
    @Modified
    protected void activate(Map<String, Object> properties) {
        Destination destination = _destination;
        messageListeners = new ArrayList<MessageListener>(destination.getMessageListeners());
        destination.unregisterMessageListeners();
        destination.register(this);
    }

    @Deactivate
    @Modified
    protected void deactivate(Map<String, Object> properties) {
        Destination destination = _destination;
        destination.unregisterMessageListeners();
        for (MessageListener messageListener : messageListeners) {
            destination.register(messageListener);
        }
    }

    @Override
    protected void doReceive(Message message) {
        // Custom send mail
    }
    
    @Reference(target = "(destination.name=liferay/mail)")
    private Destination _destination;
}