/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library 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 2.1 of the License, or (at your option) * any later version. * * This library 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. */ package com.liferay.portal.kernel.util; import com.liferay.mail.kernel.model.FileAttachment; import com.liferay.mail.kernel.model.MailMessage; import com.liferay.mail.kernel.model.SMTPAccount; import com.liferay.mail.kernel.service.MailServiceUtil; import com.liferay.mail.kernel.template.MailTemplate; import com.liferay.mail.kernel.template.MailTemplateContext; import com.liferay.mail.kernel.template.MailTemplateContextBuilder; import com.liferay.mail.kernel.template.MailTemplateFactoryUtil; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.json.JSONFactoryUtil; import com.liferay.portal.kernel.json.JSONObject; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.messaging.DestinationNames; import com.liferay.portal.kernel.messaging.MessageBusUtil; import com.liferay.portal.kernel.model.Company; import com.liferay.portal.kernel.model.Group; import com.liferay.portal.kernel.model.ResourceAction; import com.liferay.portal.kernel.model.Subscription; import com.liferay.portal.kernel.model.User; import com.liferay.portal.kernel.model.UserNotificationDeliveryConstants; import com.liferay.portal.kernel.notifications.UserNotificationManagerUtil; import com.liferay.portal.kernel.security.permission.ActionKeys; import com.liferay.portal.kernel.security.permission.BaseModelPermissionCheckerUtil; import com.liferay.portal.kernel.security.permission.PermissionChecker; import com.liferay.portal.kernel.security.permission.PermissionCheckerFactoryUtil; import com.liferay.portal.kernel.service.CompanyLocalServiceUtil; import com.liferay.portal.kernel.service.GroupLocalServiceUtil; import com.liferay.portal.kernel.service.ResourceActionLocalServiceUtil; import com.liferay.portal.kernel.service.ServiceContext; import com.liferay.portal.kernel.service.SubscriptionLocalServiceUtil; import com.liferay.portal.kernel.service.UserLocalServiceUtil; import com.liferay.portal.kernel.service.UserNotificationEventLocalServiceUtil; import com.liferay.portal.kernel.transaction.TransactionCommitCallbackUtil; import com.liferay.portal.kernel.uuid.PortalUUIDUtil; import java.io.File; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import javax.mail.internet.InternetAddress; /** * @author Brian Wing Shun Chan * @author Mate Thurzo * @author Raymond Augé * @author Sergio González * @author Roberto Díaz */ public class SubscriptionSender implements Serializable { public void addFileAttachment(File file) { addFileAttachment(file, null); } public void addFileAttachment(File file, String fileName) { if (file == null) { return; } if (fileAttachments == null) { fileAttachments = new ArrayList<>(); } FileAttachment attachment = new FileAttachment(file, fileName); fileAttachments.add(attachment); } public <T> void addHook(Hook.Event<T> event, Hook<T> hook) { List<Hook<T>> hooks = _getHooks(event); hooks.add(hook); } public void addPersistedSubscribers(String className, long classPK) { ObjectValuePair<String, Long> ovp = new ObjectValuePair<>( className, classPK); _persistestedSubscribersOVPs.add(ovp); } public void addRuntimeSubscribers(String toAddress, String toName) { ObjectValuePair<String, String> ovp = new ObjectValuePair<>( toAddress, toName); _runtimeSubscribersOVPs.add(ovp); } public void flushNotifications() throws Exception { initialize(); Thread currentThread = Thread.currentThread(); ClassLoader contextClassLoader = currentThread.getContextClassLoader(); try { if ((_classLoader != null) && (contextClassLoader != _classLoader)) { currentThread.setContextClassLoader(_classLoader); } for (ObjectValuePair<String, Long> ovp : _persistestedSubscribersOVPs) { String className = ovp.getKey(); long classPK = ovp.getValue(); List<Subscription> subscriptions = SubscriptionLocalServiceUtil.getSubscriptions( companyId, className, classPK); for (Subscription subscription : subscriptions) { try { notifyPersistedSubscriber(subscription); } catch (Exception e) { _log.error( "Unable to process subscription: " + subscription, e); } } } _persistestedSubscribersOVPs.clear(); for (ObjectValuePair<String, String> ovp : _runtimeSubscribersOVPs) { String toAddress = ovp.getKey(); if (Validator.isNull(toAddress)) { continue; } if (_sentEmailAddresses.contains(toAddress)) { if (_log.isDebugEnabled()) { _log.debug( "Do not send a duplicate email to " + toAddress); } continue; } if (_log.isDebugEnabled()) { _log.debug( "Add " + toAddress + " to the list of users who have " + "received an email"); } _sentEmailAddresses.add(toAddress); String toName = ovp.getValue(); InternetAddress to = new InternetAddress(toAddress, toName); notifyRuntimeSubscriber(to, LocaleUtil.getDefault()); } _runtimeSubscribersOVPs.clear(); if (bulk) { Locale locale = LocaleUtil.getDefault(); MailTemplateContext mailTemplateContext = _getBasicMailTemplateContext(locale); MailTemplate replyToAddressMailTemplate = MailTemplateFactoryUtil.createMailTemplate( replyToAddress, false); String processedReplyToAddress = replyToAddressMailTemplate.renderAsString( locale, mailTemplateContext); InternetAddress to = new InternetAddress( processedReplyToAddress, processedReplyToAddress); sendEmail(to, locale); } } finally { if ((_classLoader != null) && (contextClassLoader != _classLoader)) { currentThread.setContextClassLoader(contextClassLoader); } } } public void flushNotificationsAsync() { TransactionCommitCallbackUtil.registerCallback( () -> { Thread currentThread = Thread.currentThread(); _classLoader = currentThread.getContextClassLoader(); MessageBusUtil.sendMessage( DestinationNames.SUBSCRIPTION_SENDER, SubscriptionSender.this); return null; }); } public long getCompanyId() { return companyId; } public Object getContextAttribute(String key) { return _context.get(key); } public long getCurrentUserId() { return currentUserId; } public String getMailId() { return mailId; } public ServiceContext getServiceContext() { return serviceContext; } /** * @deprecated As of 7.0.0, replaced by {@link #getCurrentUserId()} */ @Deprecated public long getUserId() { return getCurrentUserId(); } public void initialize() throws Exception { if (_initialized) { return; } _initialized = true; if ((groupId == 0) && (serviceContext != null)) { setScopeGroupId(serviceContext.getScopeGroupId()); } Company company = CompanyLocalServiceUtil.getCompany(companyId); setContextAttribute("[$COMPANY_ID$]", company.getCompanyId()); setContextAttribute("[$COMPANY_MX$]", company.getMx()); setContextAttribute("[$COMPANY_NAME$]", company.getName()); setContextAttribute("[$PORTAL_URL$]", company.getPortalURL(groupId)); if (groupId > 0) { Group group = GroupLocalServiceUtil.getGroup(groupId); setContextAttribute("[$SITE_NAME$]", group.getDescriptiveName()); } if ((creatorUserId > 0) && Validator.isNotNull(_contextCreatorUserPrefix)) { setContextAttribute( "[$" + _contextCreatorUserPrefix + "_USER_ADDRESS$]", PortalUtil.getUserEmailAddress(creatorUserId)); setContextAttribute( "[$" + _contextCreatorUserPrefix + "_USER_NAME$]", PortalUtil.getUserName(creatorUserId, StringPool.BLANK)); } if (uniqueMailId) { _mailIdIds = ArrayUtil.append( _mailIdIds, PortalUUIDUtil.generate()); } mailId = PortalUtil.getMailId( company.getMx(), _mailIdPopPortletPrefix, _mailIdIds); } public boolean isBulk() { return bulk; } public void setBody(String body) { this.body = body; } public void setBulk(boolean bulk) { this.bulk = bulk; } public void setClassName(String className) { _className = className; } public void setClassPK(long classPK) { _classPK = classPK; } public void setCompanyId(long companyId) { this.companyId = companyId; } public void setContextAttribute(String key, EscapableObject<String> value) { _context.put(key, value); } public void setContextAttribute(String key, Object value) { setContextAttribute(key, value, true); } public void setContextAttribute(String key, Object value, boolean escape) { setContextAttribute( key, new HtmlEscapableObject<>(String.valueOf(value), escape)); } public void setContextAttributes(Object... values) { for (int i = 0; i < values.length; i += 2) { setContextAttribute(String.valueOf(values[i]), values[i + 1]); } } public void setContextCreatorUserPrefix(String contextCreatorUserPrefix) { _contextCreatorUserPrefix = contextCreatorUserPrefix; } public void setCreatorUserId(long creatorUserId) { this.creatorUserId = creatorUserId; } public void setCurrentUserId(long currentUserId) { this.currentUserId = currentUserId; } public void setEntryTitle(String entryTitle) { _entryTitle = entryTitle; } public void setEntryURL(String entryURL) { _entryURL = entryURL; } public void setFrom(String fromAddress, String fromName) { this.fromAddress = fromAddress; this.fromName = fromName; } public void setGroupId(long groupId) { this.groupId = groupId; } public void setHtmlFormat(boolean htmlFormat) { this.htmlFormat = htmlFormat; } public void setInReplyTo(String inReplyTo) { this.inReplyTo = inReplyTo; } public void setLocalizedBodyMap(Map<Locale, String> localizedBodyMap) { this.localizedBodyMap = localizedBodyMap; } public void setLocalizedContextAttribute( String key, EscapableLocalizableFunction value) { _localizedContext.put(key, value); } public <T extends Serializable & Function<Locale, String>> void setLocalizedContextAttribute(String key, T function) { setLocalizedContextAttribute(key, function, true); } public <T extends Serializable & Function<Locale, String>> void setLocalizedContextAttribute( String key, T function, boolean escape) { setLocalizedContextAttribute( key, new EscapableLocalizableFunction(function, escape)); } public void setLocalizedContextAttributeWithFunction( String key, Function<Locale, String> function) { setLocalizedContextAttributeWithFunction(key, function, true); } public void setLocalizedContextAttributeWithFunction( String key, Function<Locale, String> function, boolean escape) { setLocalizedContextAttribute( key, new EscapableLocalizableFunction(function, escape)); } public void setLocalizedPortletTitleMap( Map<Locale, String> localizedPortletTitleMap) { this.localizedPortletTitleMap = localizedPortletTitleMap; } public void setLocalizedSubjectMap( Map<Locale, String> localizedSubjectMap) { this.localizedSubjectMap = localizedSubjectMap; } public void setMailId(String popPortletPrefix, Object... ids) { _mailIdPopPortletPrefix = popPortletPrefix; _mailIdIds = ids; } public void setNotificationClassNameId(long notificationClassNameId) { _notificationClassNameId = notificationClassNameId; } /** * @see com.liferay.portal.kernel.notifications.UserNotificationDefinition */ public void setNotificationType(int notificationType) { _notificationType = notificationType; } public void setPortletId(String portletId) { this.portletId = portletId; } public void setReplyToAddress(String replyToAddress) { this.replyToAddress = replyToAddress; } /** * @see com.liferay.portal.kernel.search.BaseIndexer#getSiteGroupId(long) */ public void setScopeGroupId(long scopeGroupId) { try { Group group = GroupLocalServiceUtil.getGroup(scopeGroupId); if (group.isLayout()) { groupId = group.getParentGroupId(); } else { groupId = scopeGroupId; } } catch (Exception e) { } this.scopeGroupId = scopeGroupId; } public void setServiceContext(ServiceContext serviceContext) { this.serviceContext = serviceContext; } public void setSMTPAccount(SMTPAccount smtpAccount) { this.smtpAccount = smtpAccount; } public void setSubject(String subject) { this.subject = subject; } public void setUniqueMailId(boolean uniqueMailId) { this.uniqueMailId = uniqueMailId; } /** * @deprecated As of 7.0.0, replaced by {@link #setCurrentUserId(long)} */ @Deprecated public void setUserId(long userId) { setCurrentUserId(userId); } public interface Hook<T> { public void process(T payload); public interface Event<S> { public static final Event<MailMessage> MAIL_MESSAGE_CREATED = new Event<MailMessage>() {}; public static final Event<Subscription> PERSISTED_SUBSCRIBER_FOUND = new Event<Subscription>() {}; } } protected void deleteSubscription(Subscription subscription) throws Exception { SubscriptionLocalServiceUtil.deleteSubscription( subscription.getSubscriptionId()); } protected boolean hasPermission( Subscription subscription, String className, long classPK, User user) throws Exception { if (subscription.getClassName() == null) { return false; } PermissionChecker permissionChecker = PermissionCheckerFactoryUtil.create(user); Boolean hasPermission = null; if (Validator.isNotNull(className)) { hasPermission = BaseModelPermissionCheckerUtil.containsBaseModelPermission( permissionChecker, groupId, className, classPK, ActionKeys.VIEW); if ((hasPermission == null) || !hasPermission) { return false; } } hasPermission = hasSubscribePermission(permissionChecker, subscription); if ((hasPermission == null) || !hasPermission) { return false; } return true; } protected boolean hasPermission(Subscription subscription, User user) throws Exception { return hasPermission(subscription, _className, _classPK, user); } /** * @throws PortalException */ protected Boolean hasSubscribePermission( PermissionChecker permissionChecker, Subscription subscription) throws PortalException { ResourceAction resourceAction = ResourceActionLocalServiceUtil.fetchResourceAction( subscription.getClassName(), ActionKeys.SUBSCRIBE); if (resourceAction != null) { return BaseModelPermissionCheckerUtil.containsBaseModelPermission( permissionChecker, groupId, subscription.getClassName(), subscription.getClassPK(), ActionKeys.SUBSCRIBE); } return Boolean.TRUE; } protected void notifyPersistedSubscriber(Subscription subscription) throws Exception { notifyPersistedSubscriber(subscription, _className, _classPK); } protected void notifyPersistedSubscriber( Subscription subscription, String className, long classPK) throws Exception { User user = UserLocalServiceUtil.fetchUserById( subscription.getUserId()); if (user == null) { if (_log.isInfoEnabled()) { _log.info( "Subscription " + subscription.getSubscriptionId() + " is stale and will be deleted"); } deleteSubscription(subscription); return; } String emailAddress = user.getEmailAddress(); if (_sentEmailAddresses.contains(emailAddress)) { if (_log.isDebugEnabled()) { _log.debug("Do not send a duplicate email to " + emailAddress); } return; } else { if (_log.isDebugEnabled()) { _log.debug( "Add " + emailAddress + " to the list of users who have received an email"); } _sentEmailAddresses.add(emailAddress); } if (!user.isActive()) { if (_log.isDebugEnabled()) { _log.debug("Skip inactive user " + user.getUserId()); } return; } try { if (!hasPermission(subscription, className, classPK, user)) { if (_log.isDebugEnabled()) { _log.debug("Skip unauthorized user " + user.getUserId()); } return; } } catch (Exception e) { _log.error(e, e); return; } _notifyHooks(Hook.Event.PERSISTED_SUBSCRIBER_FOUND, subscription); sendNotification(user); } protected void notifyRuntimeSubscriber(InternetAddress to, Locale locale) throws Exception { String emailAddress = to.getAddress(); User user = UserLocalServiceUtil.fetchUserByEmailAddress( companyId, emailAddress); if (user == null) { if (_log.isInfoEnabled()) { _log.info( "User with email address " + emailAddress + " does not exist for company " + companyId); } if (bulk) { _addBulkAddress(to); return; } sendEmail(to, locale); } else { sendNotification(user); } } /** * @deprecated As of 7.0.0, replaced by {@link * #notifyPersistedSubscriber(Subscription)} */ @Deprecated protected void notifySubscriber( Subscription subscription, String inferredClassName, long inferredClassPK) throws Exception { notifyPersistedSubscriber( subscription, inferredClassName, inferredClassPK); } protected void populateNotificationEventJSONObject( JSONObject notificationEventJSONObject) { notificationEventJSONObject.put("className", _className); notificationEventJSONObject.put("classPK", _classPK); notificationEventJSONObject.put("entryTitle", _entryTitle); notificationEventJSONObject.put("entryURL", _entryURL); notificationEventJSONObject.put("notificationType", _notificationType); notificationEventJSONObject.put("userId", currentUserId); } protected void processMailMessage(MailMessage mailMessage, Locale locale) throws Exception { InternetAddress from = mailMessage.getFrom(); InternetAddress to = mailMessage.getTo()[0]; MailTemplateContext mailTemplateContext = _getBodyMailTemplateContext( locale, from, to); MailTemplate subjectMailTemplate = MailTemplateFactoryUtil.createMailTemplate( mailMessage.getSubject(), false); String processedSubject = subjectMailTemplate.renderAsString( locale, mailTemplateContext); mailMessage.setSubject(processedSubject); MailTemplate bodyMailTemplate = MailTemplateFactoryUtil.createMailTemplate( mailMessage.getBody(), true); String processedBody = bodyMailTemplate.renderAsString( locale, mailTemplateContext); mailMessage.setBody(processedBody); _notifyHooks(Hook.Event.MAIL_MESSAGE_CREATED, mailMessage); } /** * @deprecated As of 7.0.0, with no direct replacement */ @Deprecated protected String replaceContent(String content, Locale locale) throws Exception { return replaceContent(content, locale, true); } /** * @deprecated As of 7.0.0, with no direct replacement */ @Deprecated protected String replaceContent( String content, Locale locale, boolean escape) throws Exception { MailTemplateContext mailTemplateContext = _getBasicMailTemplateContext( locale); MailTemplate mailTemplate = MailTemplateFactoryUtil.createMailTemplate( content, escape); return mailTemplate.renderAsString(locale, mailTemplateContext); } protected void sendEmail(InternetAddress to, Locale locale) throws Exception { MailTemplateContext mailTemplateContext = _getBasicMailTemplateContext( locale); MailTemplate fromAddressMailTemplate = MailTemplateFactoryUtil.createMailTemplate(fromAddress, false); MailTemplate fromNameMailTemplate = MailTemplateFactoryUtil.createMailTemplate(fromName, false); InternetAddress from = new InternetAddress( fromAddressMailTemplate.renderAsString(locale, mailTemplateContext), fromNameMailTemplate.renderAsString(locale, mailTemplateContext)); String processedSubject = _getLocalizedValue( localizedSubjectMap, locale, subject); String processedBody = _getLocalizedValue( localizedBodyMap, locale, body); MailMessage mailMessage = new MailMessage( from, to, processedSubject, processedBody, htmlFormat); if (fileAttachments != null) { for (FileAttachment fileAttachment : fileAttachments) { mailMessage.addFileAttachment( fileAttachment.getFile(), fileAttachment.getFileName()); } } if (bulk && (_bulkAddresses != null)) { mailMessage.setBulkAddresses( _bulkAddresses.toArray( new InternetAddress[_bulkAddresses.size()])); _bulkAddresses.clear(); } if (inReplyTo != null) { mailMessage.setInReplyTo(inReplyTo); } mailMessage.setMessageId(mailId); if (replyToAddress != null) { MailTemplate replyToAddressMailTemplate = MailTemplateFactoryUtil.createMailTemplate( replyToAddress, false); String processedReplyToAddress = replyToAddressMailTemplate.renderAsString( locale, mailTemplateContext); InternetAddress replyTo = new InternetAddress( processedReplyToAddress, processedReplyToAddress); mailMessage.setReplyTo(new InternetAddress[] {replyTo}); } if (smtpAccount != null) { mailMessage.setSMTPAccount(smtpAccount); } processMailMessage(mailMessage, locale); MailServiceUtil.sendEmail(mailMessage); } protected void sendEmailNotification(User user) throws Exception { if (UserNotificationManagerUtil.isDeliver( user.getUserId(), portletId, _notificationClassNameId, _notificationType, UserNotificationDeliveryConstants.TYPE_EMAIL)) { InternetAddress to = new InternetAddress( user.getEmailAddress(), user.getFullName()); if (bulk) { _addBulkAddress(to); return; } sendEmail(to, user.getLocale()); } } protected void sendNotification(User user) throws Exception { if (currentUserId == user.getUserId()) { if (_log.isDebugEnabled()) { _log.debug("Skip user " + currentUserId); } return; } sendEmailNotification(user); sendUserNotification(user); } protected void sendUserNotification(User user) throws Exception { JSONObject notificationEventJSONObject = JSONFactoryUtil.createJSONObject(); populateNotificationEventJSONObject(notificationEventJSONObject); if (UserNotificationManagerUtil.isDeliver( user.getUserId(), portletId, _notificationClassNameId, _notificationType, UserNotificationDeliveryConstants.TYPE_PUSH)) { UserNotificationEventLocalServiceUtil.sendUserNotificationEvents( user.getUserId(), portletId, UserNotificationDeliveryConstants.TYPE_PUSH, notificationEventJSONObject); } if (UserNotificationManagerUtil.isDeliver( user.getUserId(), portletId, _notificationClassNameId, _notificationType, UserNotificationDeliveryConstants.TYPE_WEBSITE)) { UserNotificationEventLocalServiceUtil.sendUserNotificationEvents( user.getUserId(), portletId, UserNotificationDeliveryConstants.TYPE_WEBSITE, notificationEventJSONObject); } } protected String body; protected boolean bulk; protected long companyId; protected long creatorUserId; protected long currentUserId; protected List<FileAttachment> fileAttachments = new ArrayList<>(); protected String fromAddress; protected String fromName; protected long groupId; protected boolean htmlFormat; protected String inReplyTo; protected Map<Locale, String> localizedBodyMap; protected Map<Locale, String> localizedPortletTitleMap; protected Map<Locale, String> localizedSubjectMap; protected String mailId; protected String portletId; protected String replyToAddress; protected long scopeGroupId; protected ServiceContext serviceContext; protected SMTPAccount smtpAccount; protected String subject; protected boolean uniqueMailId = true; private void _addBulkAddress(InternetAddress internetAddress) { if (_bulkAddresses == null) { _bulkAddresses = new ArrayList<>(); } _bulkAddresses.add(internetAddress); } private MailTemplateContext _getBasicMailTemplateContext(Locale locale) { MailTemplateContextBuilder mailTemplateContextBuilder = MailTemplateFactoryUtil.createMailTemplateContextBuilder(); String portletName = _getPortletName(locale); mailTemplateContextBuilder.put("[$PORTLET_NAME$]", portletName); mailTemplateContextBuilder.put( "[$PORTLET_TITLE$]", _getPortletTitle(portletName, locale)); _context.forEach(mailTemplateContextBuilder::put); _localizedContext.forEach(mailTemplateContextBuilder::put); return mailTemplateContextBuilder.build(); } private MailTemplateContext _getBodyMailTemplateContext( Locale locale, InternetAddress from, InternetAddress to) { MailTemplateContextBuilder mailTemplateContextBuilder = MailTemplateFactoryUtil.createMailTemplateContextBuilder(); mailTemplateContextBuilder.put("[$FROM_ADDRESS$]", from.getAddress()); mailTemplateContextBuilder.put( "[$FROM_NAME$]", GetterUtil.getString(from.getPersonal(), from.getAddress())); mailTemplateContextBuilder.put( "[$TO_ADDRESS$]", HtmlUtil.escape(to.getAddress())); mailTemplateContextBuilder.put( "[$TO_NAME$]", HtmlUtil.escape( GetterUtil.getString(to.getPersonal(), to.getAddress()))); MailTemplateContext mailTemplateContext = mailTemplateContextBuilder.build(); return mailTemplateContext.aggregateWith( _getBasicMailTemplateContext(locale)); } private <T> List<Hook<T>> _getHooks(Hook.Event<T> event) { return (List)_hooks.computeIfAbsent(event, key -> new ArrayList<>()); } private String _getLocalizedValue( Map<Locale, String> localizedValueMap, Locale locale, String defaultValue) { return GetterUtil.get( MapUtil.getWithFallbackKey( localizedValueMap, locale, LocaleUtil.getDefault()), defaultValue); } private String _getPortletName(Locale locale) { if (Validator.isNull(portletId)) { return StringPool.BLANK; } return PortalUtil.getPortletTitle(portletId, locale); } private String _getPortletTitle(String portletName, Locale locale) { return _getLocalizedValue( localizedPortletTitleMap, locale, portletName); } private <T> void _notifyHooks(Hook.Event<T> event, T payload) { List<Hook<T>> hooks = _getHooks(event); hooks.forEach(hook -> hook.process(payload)); } private void readObject(ObjectInputStream objectInputStream) throws ClassNotFoundException, IOException { objectInputStream.defaultReadObject(); String servletContextName = objectInputStream.readUTF(); if (!servletContextName.isEmpty()) { _classLoader = ClassLoaderPool.getClassLoader(servletContextName); } } private void writeObject(ObjectOutputStream objectOutputStream) throws IOException { objectOutputStream.defaultWriteObject(); String servletContextName = StringPool.BLANK; if (_classLoader != null) { servletContextName = ClassLoaderPool.getContextName(_classLoader); } objectOutputStream.writeUTF(servletContextName); } private static final Log _log = LogFactoryUtil.getLog( SubscriptionSender.class); private List<InternetAddress> _bulkAddresses; private transient ClassLoader _classLoader; private String _className; private long _classPK; private final Map<String, EscapableObject<String>> _context = new HashMap<>(); private String _contextCreatorUserPrefix; private String _entryTitle; private String _entryURL; private final Map<Hook.Event<?>, List<Hook<?>>> _hooks = new HashMap<>(); private boolean _initialized; private final Map<String, EscapableLocalizableFunction> _localizedContext = new HashMap<>(); private Object[] _mailIdIds; private String _mailIdPopPortletPrefix; private long _notificationClassNameId; private int _notificationType; private final List<ObjectValuePair<String, Long>> _persistestedSubscribersOVPs = new ArrayList<>(); private final List<ObjectValuePair<String, String>> _runtimeSubscribersOVPs = new ArrayList<>(); private final Set<String> _sentEmailAddresses = new HashSet<>(); }