You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by si...@apache.org on 2006/11/24 16:59:44 UTC
svn commit: r478902 - in /incubator/ofbiz/trunk/applications:
marketing/entitydef/ party/config/ party/servicedef/
party/src/org/ofbiz/party/communication/
Author: sichen
Date: Fri Nov 24 07:59:43 2006
New Revision: 478902
URL: http://svn.apache.org/viewvc?view=rev&rev=478902
Log:
Improved sending of email to contact lists to better handle cases where email addresses are bad or if the email aborts in the middle
Modified:
incubator/ofbiz/trunk/applications/marketing/entitydef/entitygroup.xml
incubator/ofbiz/trunk/applications/marketing/entitydef/entitymodel.xml
incubator/ofbiz/trunk/applications/party/config/PartyUiLabels.properties
incubator/ofbiz/trunk/applications/party/servicedef/services.xml
incubator/ofbiz/trunk/applications/party/src/org/ofbiz/party/communication/CommunicationEventServices.java
Modified: incubator/ofbiz/trunk/applications/marketing/entitydef/entitygroup.xml
URL: http://svn.apache.org/viewvc/incubator/ofbiz/trunk/applications/marketing/entitydef/entitygroup.xml?view=diff&rev=478902&r1=478901&r2=478902
==============================================================================
--- incubator/ofbiz/trunk/applications/marketing/entitydef/entitygroup.xml (original)
+++ incubator/ofbiz/trunk/applications/marketing/entitydef/entitygroup.xml Fri Nov 24 07:59:43 2006
@@ -34,6 +34,7 @@
<!-- ========================================================= -->
<entity-group group="org.ofbiz" entity="ContactList" />
+ <entity-group group="org.ofbiz" entity="ContactListCommStatus" />
<entity-group group="org.ofbiz" entity="ContactListParty" />
<entity-group group="org.ofbiz" entity="ContactListPartyAndStatus" />
<entity-group group="org.ofbiz" entity="ContactListPartyStatus" />
Modified: incubator/ofbiz/trunk/applications/marketing/entitydef/entitymodel.xml
URL: http://svn.apache.org/viewvc/incubator/ofbiz/trunk/applications/marketing/entitydef/entitymodel.xml?view=diff&rev=478902&r1=478901&r2=478902
==============================================================================
--- incubator/ofbiz/trunk/applications/marketing/entitydef/entitymodel.xml (original)
+++ incubator/ofbiz/trunk/applications/marketing/entitydef/entitymodel.xml Fri Nov 24 07:59:43 2006
@@ -174,6 +174,29 @@
<key-map field-name="ownerPartyId" rel-field-name="partyId"/>
</relation>
</entity>
+ <entity entity-name="ContactListCommStatus"
+ package-name="org.ofbiz.marketing.contact"
+ title="Contact List Entity">
+ <field name="contactListId" type="id-ne"></field>
+ <field name="communicationEventId" type="id-ne"></field>
+ <field name="contactMechId" type="id-ne"></field>
+ <field name="statusId" type="id-ne"></field>
+ <prim-key field="contactListId"/>
+ <prim-key field="communicationEventId"/>
+ <prim-key field="contactMechId"/>
+ <relation type="one" fk-name="CNCT_LST_CST_CL" rel-entity-name="ContactList">
+ <key-map field-name="contactListId"/>
+ </relation>
+ <relation type="one" fk-name="CNCT_LST_CST_CE" rel-entity-name="CommunicationEvent">
+ <key-map field-name="communicationEventId"/>
+ </relation>
+ <relation type="one" fk-name="CNCT_LST_CST_CM" rel-entity-name="ContactMech">
+ <key-map field-name="contactMechId"/>
+ </relation>
+ <relation type="one" fk-name="CNCT_LST_CST_ST" rel-entity-name="StatusItem">
+ <key-map field-name="statusId"/>
+ </relation>
+ </entity>
<entity entity-name="ContactListParty"
package-name="org.ofbiz.marketing.contact"
title="Contact List Party Entity">
Modified: incubator/ofbiz/trunk/applications/party/config/PartyUiLabels.properties
URL: http://svn.apache.org/viewvc/incubator/ofbiz/trunk/applications/party/config/PartyUiLabels.properties?view=diff&rev=478902&r1=478901&r2=478902
==============================================================================
--- incubator/ofbiz/trunk/applications/party/config/PartyUiLabels.properties (original)
+++ incubator/ofbiz/trunk/applications/party/config/PartyUiLabels.properties Fri Nov 24 07:59:43 2006
@@ -34,6 +34,11 @@
commeventservices.communication_event_must_be_email_for_email=ERROR: Communication event is not an email communication and cannot be emailed for communication event Id
commeventservices.communication_event_from_contact_mech_must_be_email=ERROR: Communication event must have a from contact mech that is an email for comm event Id
commeventservices.communication_event_to_contact_mech_must_be_email=ERROR: Communication event must have a to contact mech that is an email for comm event Id
+commeventservices.errorCallingSendEmailToContactListService=Error calling sendEmailToContactList service
+commeventservices.errorInSendEmailToContactListService=Error in sendEmailToContactList service
+commeventservices.errorCallingUpdateContactListPartyService=Error calling updateContactListParty service
+commeventservices.errorCallingSendMailService=Error calling sendMail service
+commeventservices.skippingInvalidEmailAddress=Skipping invalid email address
contactmechservices.could_not_create_contact_info_id_generation_failure=ERROR: Could not create contact info (id generation failure).
contactmechservices.service_createContactMech_not_be_used_for_POSTAL_ADDRESS=This service (createContactMech) should not be used for POSTAL_ADDRESS type ContactMechs, use the createPostalAddress service.
Modified: incubator/ofbiz/trunk/applications/party/servicedef/services.xml
URL: http://svn.apache.org/viewvc/incubator/ofbiz/trunk/applications/party/servicedef/services.xml?view=diff&rev=478902&r1=478901&r2=478902
==============================================================================
--- incubator/ofbiz/trunk/applications/party/servicedef/services.xml (original)
+++ incubator/ofbiz/trunk/applications/party/servicedef/services.xml Fri Nov 24 07:59:43 2006
@@ -639,10 +639,20 @@
location="org.ofbiz.party.communication.CommunicationEventServices" invoke="sendCommEventAsEmail" auth="true"
transaction-timeout="7200"> <!-- set transaction time out for 2 hours, since this sometimes may run as an async service to send emails to lots of people -->
<description>Sends a communication event as a single-part email using sendMail. All parameters come from CommunicationEvent, which must
- be of type EMAIL_COMMUNICATION. If there a contactListId, then will send it to every party on that contact list at their
- preferredContactMechId. Otherwise, will look for a contactMechIdTo to send the emails</description>
+ be of type EMAIL_COMMUNICATION. Will look for a contactMechIdTo to send the emails</description>
<attribute name="communicationEventId" type="String" mode="IN" optional="false"/>
</service>
+
+ <service name="sendEmailToContactList" engine="java"
+ location="org.ofbiz.party.communication.CommunicationEventServices" invoke="sendEmailToContactList" auth="true"
+ use-transaction="false" max-retry="3"> <!-- Individual emails will be wrapped in their own transactions -->
+ <description>Send emails to members of a contact list, wrapping each email in its own transaction and tagging each member
+ that has been sent, so if the whole effort is aborted, it can start over from the middle. The max-retry is important because if this service is
+ and some emails cannot sent, it will start again later and try again</description>
+ <attribute name="contactListId" type="String" mode="IN" optional="false"/>
+ <attribute name="communicationEventId" type="String" mode="IN" optional="false"/>
+ </service>
+
<service name="setCommEventComplete" engine="java"
location="org.ofbiz.party.communication.CommunicationEventServices" invoke="setCommEventComplete" auth="true">
<description>Sets the status of a communication event to COM_COMPLETE using the updateCommunicationEvent service</description>
Modified: incubator/ofbiz/trunk/applications/party/src/org/ofbiz/party/communication/CommunicationEventServices.java
URL: http://svn.apache.org/viewvc/incubator/ofbiz/trunk/applications/party/src/org/ofbiz/party/communication/CommunicationEventServices.java?view=diff&rev=478902&r1=478901&r2=478902
==============================================================================
--- incubator/ofbiz/trunk/applications/party/src/org/ofbiz/party/communication/CommunicationEventServices.java (original)
+++ incubator/ofbiz/trunk/applications/party/src/org/ofbiz/party/communication/CommunicationEventServices.java Fri Nov 24 07:59:43 2006
@@ -20,9 +20,7 @@
import java.util.*;
import java.sql.Timestamp;
-import org.ofbiz.base.util.UtilDateTime;
-import org.ofbiz.base.util.UtilMisc;
-import org.ofbiz.base.util.UtilProperties;
+import org.ofbiz.base.util.*;
import org.ofbiz.entity.GenericDelegator;
import org.ofbiz.entity.GenericEntityException;
import org.ofbiz.entity.GenericValue;
@@ -103,41 +101,107 @@
errorMessages.add(ServiceUtil.getErrorMessage(tmpResult));
}
} else {
- // there's actually a contact list here, so we want to be sending to the entire contact list
- GenericValue contactList = communicationEvent.getRelatedOne("ContactList");
- // set up some variables for single use lists
- boolean singleUse = ("Y".equals(contactList.get("singleUse")) ? true : false);
- Timestamp now = UtilDateTime.nowTimestamp();
-
- // find a list of distinct email addresses from active, ACCEPTED parties in the contact list
- // using a list iterator (because there can be a large number)
- List conditionList = UtilMisc.toList(
- new EntityExpr("contactListId", EntityOperator.EQUALS, contactList.get("contactListId")),
- new EntityExpr("statusId", EntityOperator.EQUALS, "CLPT_ACCEPTED"),
- new EntityExpr("preferredContactMechId", EntityOperator.NOT_EQUAL, null),
- EntityUtil.getFilterByDateExpr()
- );
- EntityConditionList conditions = new EntityConditionList(conditionList, EntityOperator.AND);
- List fieldsToSelect = UtilMisc.toList("infoString");
- EntityListIterator sendToEmailsIt = delegator.findListIteratorByCondition("ContactListPartyAndContactMech", conditions, null, fieldsToSelect, null,
- new EntityFindOptions(true, EntityFindOptions.TYPE_SCROLL_INSENSITIVE, EntityFindOptions.CONCUR_READ_ONLY, true));
-
- // send an email to each contact list member
+ // Call the sendEmailToContactList service if there's a contactListId present
+ Map sendEmailToContactListContext = new HashMap();
+ sendEmailToContactListContext.put("contactListId", communicationEvent.getString("contactListId"));
+ sendEmailToContactListContext.put("communicationEventId", communicationEventId);
+ sendEmailToContactListContext.put("userLogin", userLogin);
+ try {
+ dispatcher.runAsync("sendEmailToContactList", sendEmailToContactListContext);
+ } catch( GenericServiceException e ) {
+ String errMsg = UtilProperties.getMessage(resource, "commeventservices.errorCallingSendEmailToContactListService", locale);
+ Debug.logError(e, errMsg, module);
+ errorMessages.add(errMsg);
+ errorMessages.addAll(e.getMessageList());
+ }
+ }
+ } catch (GenericEntityException eex) {
+ ServiceUtil.returnError(eex.getMessage());
+ } catch (GenericServiceException esx) {
+ ServiceUtil.returnError(esx.getMessage());
+ }
+
+ // If there were errors, then the result of this service should be error with the full list of messages
+ if (errorMessages.size() > 0) {
+ result = ServiceUtil.returnError(errorMessages);
+ }
+ return result;
+ }
+
+ public static Map sendEmailToContactList(DispatchContext ctx, Map context) {
+ GenericDelegator delegator = ctx.getDelegator();
+ LocalDispatcher dispatcher = ctx.getDispatcher();
+ GenericValue userLogin = (GenericValue) context.get("userLogin");
+ Locale locale = (Locale) context.get("locale");
+
+ List errorMessages = new ArrayList();
+ String errorCallingUpdateContactListPartyService = UtilProperties.getMessage(resource, "commeventservices.errorCallingUpdateContactListPartyService", locale);
+ String errorCallingSendMailService = UtilProperties.getMessage(resource, "commeventservices.errorCallingSendMailService", locale);
+ String errorInSendEmailToContactListService = UtilProperties.getMessage(resource, "commeventservices.errorForEmailAddress", locale);
+ String skippingInvalidEmailAddress = UtilProperties.getMessage(resource, "commeventservices.skippingInvalidEmailAddress", locale);
+
+ String contactListId = (String) context.get("contactListId");
+ String communicationEventId = (String) context.get("communicationEventId");
+
+ // Any exceptions thrown in this block will cause the service to return error
+ try {
+
+ GenericValue communicationEvent = delegator.findByPrimaryKey("CommunicationEvent", UtilMisc.toMap("communicationEventId", communicationEventId));
+ GenericValue contactList = delegator.findByPrimaryKey("ContactList", UtilMisc.toMap("contactListId", contactListId));
+
+ Map sendMailParams = new HashMap();
+ sendMailParams.put("sendFrom", communicationEvent.getRelatedOne("FromContactMech").getString("infoString"));
+ sendMailParams.put("subject", communicationEvent.getString("subject"));
+ sendMailParams.put("body", communicationEvent.getString("content"));
+ sendMailParams.put("contentType", communicationEvent.getString("contentMimeTypeId"));
+ sendMailParams.put("userLogin", userLogin);
+
+ // Find a list of distinct email addresses from active, ACCEPTED parties in the contact list
+ // using a list iterator (because there can be a large number)
+ List conditionList = UtilMisc.toList(
+ new EntityExpr("contactListId", EntityOperator.EQUALS, contactList.get("contactListId")),
+ new EntityExpr("statusId", EntityOperator.EQUALS, "CLPT_ACCEPTED"),
+ new EntityExpr("preferredContactMechId", EntityOperator.NOT_EQUAL, null),
+ EntityUtil.getFilterByDateExpr()
+ );
+ EntityConditionList conditions = new EntityConditionList(conditionList, EntityOperator.AND);
+ List fieldsToSelect = UtilMisc.toList("infoString");
+
+ List sendToEmails = delegator.findByCondition("ContactListPartyAndContactMech", conditions, null, fieldsToSelect, null,
+ new EntityFindOptions(true, EntityFindOptions.TYPE_SCROLL_INSENSITIVE, EntityFindOptions.CONCUR_READ_ONLY, true));
+
+ // Send an email to each contact list member
+
+ List orderBy = UtilMisc.toList("-fromDate");
+ Iterator sendToEmailsIt = sendToEmails.iterator();
+ while (sendToEmailsIt.hasNext()) {
- List orderBy = UtilMisc.toList("-fromDate");
- GenericValue contactListPartyAndContactMech = null ;
- while ((contactListPartyAndContactMech = (GenericValue) sendToEmailsIt.next()) != null) {
+ GenericValue contactListPartyAndContactMech = (GenericValue) sendToEmailsIt.next();
+
+ // Any exceptions thrown in this inner block will only relate to a single email of the list, so should
+ // only be logged and not cause the service to return an error
+ try {
+
String emailAddress = contactListPartyAndContactMech.getString("infoString");
if (emailAddress == null) continue;
-
+ emailAddress = emailAddress.trim();
+
+ if (! UtilValidate.isEmail(emailAddress, true)) {
+
+ // If validation fails, just log and skip the email address
+ Debug.logError(skippingInvalidEmailAddress + ": " + emailAddress, module);
+ errorMessages.add(skippingInvalidEmailAddress + ": " + emailAddress);
+ continue;
+ }
+
// Because we're retrieving infoString only above (so as not to pollute the distinctness), we
// need to retrieve the partyId it's related to. Since this could be multiple parties, get
// only the most recent valid one via ContactListPartyAndContactMech.
List clpConditionList = new ArrayList(conditionList);
clpConditionList.add(new EntityExpr("infoString", EntityOperator.EQUALS, emailAddress));
EntityConditionList clpConditions = new EntityConditionList(clpConditionList, EntityOperator.AND);
-
+
List emailCLPaCMs = delegator.findByConditionCache("ContactListPartyAndContactMech", clpConditions, null, orderBy);
GenericValue lastContactListPartyACM = EntityUtil.getFirst(emailCLPaCMs);
if (lastContactListPartyACM == null) continue;
@@ -147,38 +211,75 @@
sendMailParams.put("sendTo", emailAddress);
sendMailParams.put("partyId", partyId);
- // no communicationEventId here - we want to create a communication event for each member of the contact list
-
- // could be run async as well, but that may spawn a lot of processes if there's a large list and cause problems
- Map tmpResult = dispatcher.runSync("sendMail", sendMailParams);
- if (ServiceUtil.isError(tmpResult)) {
- errorMessages.add(ServiceUtil.getErrorMessage(tmpResult));
- } else if (singleUse) {
- // expire the ContactListParty if the list is single use and sendEmail finishes successfully
+ if (! contactList.getString("contactListTypeId").equals("NEWSLETTER")) {
+ sendMailParams.put("communicationEventId", communicationEventId);
+ }
+
+ // Retrieve a record for this contactMechId from ContactListCommStatus
+ Map contactListCommStatusRecordMap = UtilMisc.toMap("contactListId", contactListId, "communicationEventId", communicationEventId, "contactMechId", lastContactListPartyACM.getString("preferredContactMechId"));
+ GenericValue contactListCommStatusRecord = delegator.findByPrimaryKey("ContactListCommStatus", contactListCommStatusRecordMap);
+ if (contactListCommStatusRecord == null) {
+
+ // No attempt has been made previously to send to this address, so create a record to reflect
+ // the beginning of the current attempt
+ Map newContactListCommStatusRecordMap = new HashMap(contactListCommStatusRecordMap);
+ newContactListCommStatusRecordMap.put("statusId", "COM_IN_PROGRESS");
+ contactListCommStatusRecord = delegator.create("ContactListCommStatus", newContactListCommStatusRecordMap);
+ } else if (contactListCommStatusRecord.get("statusId") != null && contactListCommStatusRecord.getString("statusId").equals("COM_COMPLETE")) {
+
+ // There was a successful earlier attempt, so skip this address
+ continue;
+ }
+
+ Map tmpResult = null;
+
+ // Make the attempt to send the email to the address
+ tmpResult = dispatcher.runSync("sendMail", sendMailParams);
+ if (tmpResult == null || ServiceUtil.isError(tmpResult)) {
+
+ // If the send attempt fails, just log and skip the email address
+ Debug.logError(errorCallingSendMailService + ": " + ServiceUtil.getErrorMessage(tmpResult), module);
+ errorMessages.add(errorCallingSendMailService + ": " + ServiceUtil.getErrorMessage(tmpResult));
+ continue;
+ }
+
+ if ("Y".equals(contactList.get("singleUse"))) {
+
+ // Expire the ContactListParty if the list is single use and sendEmail finishes successfully
tmpResult = dispatcher.runSync("updateContactListParty", UtilMisc.toMap("contactListId", lastContactListPartyACM.get("contactListId"),
- "partyId", partyId, "fromDate", lastContactListPartyACM.get("fromDate"),
- "thruDate", now, "userLogin", userLogin));
+ "partyId", partyId, "fromDate", lastContactListPartyACM.get("fromDate"),
+ "thruDate", UtilDateTime.nowTimestamp(), "userLogin", userLogin));
if (ServiceUtil.isError(tmpResult)) {
- errorMessages.add(ServiceUtil.getErrorMessage(tmpResult));
+
+ // If the expiry fails, just log and skip the email address
+ Debug.logError(errorCallingUpdateContactListPartyService + ": " + ServiceUtil.getErrorMessage(tmpResult), module);
+ errorMessages.add(errorCallingUpdateContactListPartyService + ": " + ServiceUtil.getErrorMessage(tmpResult));
+ continue;
}
}
-
+
+ // All is successful, so update the ContactListCommStatus record
+ contactListCommStatusRecord.set("statusId", "COM_COMPLETE");
+ delegator.store(contactListCommStatusRecord);
+
+ // Don't return a service error just because of failure for one address - just log the error and continue
+ } catch (GenericEntityException nonFatalGEE) {
+ Debug.logError(nonFatalGEE, errorInSendEmailToContactListService, module);
+ errorMessages.add(errorInSendEmailToContactListService + ": " + nonFatalGEE.getMessage());
+ } catch (GenericServiceException nonFatalGSE) {
+ Debug.logError(nonFatalGSE, errorInSendEmailToContactListService, module);
+ errorMessages.add(errorInSendEmailToContactListService + ": " + nonFatalGSE.getMessage());
}
- sendToEmailsIt.close();
}
- } catch (GenericEntityException eex) {
- ServiceUtil.returnError(eex.getMessage());
- } catch (GenericServiceException esx) {
- ServiceUtil.returnError(esx.getMessage());
- }
-
- // if there were errors, then the result of this service should be error with the full list of messages
- if (errorMessages.size() > 0) {
- result = ServiceUtil.returnError(errorMessages);
+
+ } catch (GenericEntityException fatalGEE) {
+ ServiceUtil.returnError(fatalGEE.getMessage());
}
- return result;
+
+ return errorMessages.size() == 0 ? ServiceUtil.returnSuccess() : ServiceUtil.returnError(errorMessages);
}
-
+
+
public static Map setCommEventComplete(DispatchContext dctx, Map context) {
LocalDispatcher dispatcher = dctx.getDispatcher();
GenericValue userLogin = (GenericValue) context.get("userLogin");