You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2015/01/04 20:29:34 UTC

[3/4] isis git commit: ISIS-987: moved EmailNotificationService to "userreg" package; factored out new EmailService, left in the "service.email" package.

ISIS-987: moved EmailNotificationService to "userreg" package; factored out new EmailService, left in the "service.email" package.

Note that the system properties have been renamed, from "isis.notification.email.xxx" to "isis.service.email.xxx".

Also minor further tweaks to the templates.


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/65e998de
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/65e998de
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/65e998de

Branch: refs/heads/ISIS-987
Commit: 65e998de9e15d1317a49b7dfdb9d9ea656ef09b1
Parents: ef48bd0
Author: Dan Haywood <da...@haywood-associates.co.uk>
Authored: Sun Jan 4 16:28:43 2015 +0000
Committer: Dan Haywood <da...@haywood-associates.co.uk>
Committed: Sun Jan 4 16:28:43 2015 +0000

----------------------------------------------------------------------
 .../viewer/wicket/viewer/IsisWicketModule.java  |  13 +-
 .../password_reset/PasswordResetEmailPanel.java |  20 ++-
 .../accmngt/signup/RegistrationFormPanel.java   |  14 +-
 .../email/EmailNotificationService.java         |  23 ---
 .../applib/services/email/EmailService.java     |  19 ++
 .../email/events/EmailEventAbstract.java        |  32 ----
 .../email/events/EmailRegistrationEvent.java    |  15 --
 .../email/events/PasswordResetEvent.java        |  15 --
 .../userreg/EmailNotificationService.java       |  23 +++
 .../userreg/events/EmailEventAbstract.java      |  32 ++++
 .../userreg/events/EmailRegistrationEvent.java  |  15 ++
 .../userreg/events/PasswordResetEvent.java      |  15 ++
 .../email/EmailNotificationServiceDefault.java  | 120 +++----------
 .../services/email/EmailServiceDefault.java     | 174 +++++++++++++++++++
 .../email/EmailVerificationTemplate.html        |   6 +-
 .../services/email/PasswordResetTemplate.html   |   6 +-
 16 files changed, 347 insertions(+), 195 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/65e998de/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/IsisWicketModule.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/IsisWicketModule.java b/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/IsisWicketModule.java
index 947e068..240f5ec 100644
--- a/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/IsisWicketModule.java
+++ b/component/viewer/wicket/impl/src/main/java/org/apache/isis/viewer/wicket/viewer/IsisWicketModule.java
@@ -22,9 +22,10 @@ package org.apache.isis.viewer.wicket.viewer;
 import com.google.inject.AbstractModule;
 import com.google.inject.name.Names;
 import com.google.inject.util.Providers;
-
-import org.apache.isis.applib.services.email.EmailNotificationService;
+import org.apache.isis.applib.services.email.EmailService;
+import org.apache.isis.applib.services.userreg.EmailNotificationService;
 import org.apache.isis.core.runtime.services.email.EmailNotificationServiceDefault;
+import org.apache.isis.core.runtime.services.email.EmailServiceDefault;
 import org.apache.isis.viewer.wicket.model.isis.WicketViewerSettings;
 import org.apache.isis.viewer.wicket.model.models.ImageResourceCache;
 import org.apache.isis.viewer.wicket.ui.app.registry.ComponentFactoryRegistrar;
@@ -70,7 +71,13 @@ public class IsisWicketModule extends AbstractModule {
         bind(ComponentFactoryRegistrar.class).to(ComponentFactoryRegistrarDefault.class);
         bind(ImageResourceCache.class).to(ImageResourceCacheClassPath.class);
         bind(WicketViewerSettings.class).to(WicketViewerSettingsDefault.class);
-        bind(EmailNotificationService.class).to(EmailNotificationServiceDefault.class);
+
+        // these services need to be bound because they injected directly into
+        // Wicket panels outside of the Isis runtime.
+        bind(EmailService.class)
+                .to(EmailServiceDefault.class);
+        bind(EmailNotificationService.class)
+                .to(EmailNotificationServiceDefault.class);
 
         bind(String.class).annotatedWith(Names.named("applicationName")).toInstance("Apache Isis Wicket Viewer");
         bind(String.class).annotatedWith(Names.named("applicationCss")).toProvider(Providers.of((String)null));

http://git-wip-us.apache.org/repos/asf/isis/blob/65e998de/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/accmngt/password_reset/PasswordResetEmailPanel.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/accmngt/password_reset/PasswordResetEmailPanel.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/accmngt/password_reset/PasswordResetEmailPanel.java
index e7a1899..8be7527 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/accmngt/password_reset/PasswordResetEmailPanel.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/accmngt/password_reset/PasswordResetEmailPanel.java
@@ -35,8 +35,9 @@ import org.apache.wicket.request.Url;
 import org.apache.wicket.request.UrlRenderer;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.validation.validator.EmailAddressValidator;
-import org.apache.isis.applib.services.email.EmailNotificationService;
-import org.apache.isis.applib.services.email.events.PasswordResetEvent;
+import org.apache.isis.applib.services.email.EmailService;
+import org.apache.isis.applib.services.userreg.EmailNotificationService;
+import org.apache.isis.applib.services.userreg.events.PasswordResetEvent;
 import org.apache.isis.viewer.wicket.ui.components.widgets.bootstrap.FormGroup;
 import org.apache.isis.viewer.wicket.ui.pages.accmngt.AccountConfirmationMap;
 import org.apache.isis.viewer.wicket.ui.pages.accmngt.EmailAvailableValidator;
@@ -77,8 +78,17 @@ public class PasswordResetEmailPanel extends Panel {
 
                 String confirmationUrl = createUrl(email);
 
+                /**
+                 * We have to init() the services here because the Isis runtime is not available to us
+                 * (guice will have instantiated a new instance of the service).
+                 *
+                 * We do it this way just so that the programming model for the EmailService is similar to regular Isis-managed services.
+                 */
+                emailNotificationService.init();
+                emailService.init();
+
                 final PasswordResetEvent passwordResetEvent = new PasswordResetEvent(email, confirmationUrl, applicationName);
-                boolean emailSent = emailService.send(passwordResetEvent);
+                boolean emailSent = emailNotificationService.send(passwordResetEvent);
                 if (emailSent) {
                     Map<String, String> map = new HashMap<>();
                     map.put("email", email);
@@ -110,7 +120,9 @@ public class PasswordResetEmailPanel extends Panel {
     }
 
     @Inject
-    private EmailNotificationService emailService;
+    private EmailNotificationService emailNotificationService;
+    @Inject
+    private EmailService emailService;
 
     @com.google.inject.Inject
     @Named("applicationName")

http://git-wip-us.apache.org/repos/asf/isis/blob/65e998de/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/accmngt/signup/RegistrationFormPanel.java
----------------------------------------------------------------------
diff --git a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/accmngt/signup/RegistrationFormPanel.java b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/accmngt/signup/RegistrationFormPanel.java
index c1a55b4..0a00a0c 100644
--- a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/accmngt/signup/RegistrationFormPanel.java
+++ b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/pages/accmngt/signup/RegistrationFormPanel.java
@@ -36,8 +36,9 @@ import org.apache.wicket.request.Url;
 import org.apache.wicket.request.UrlRenderer;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.validation.validator.EmailAddressValidator;
-import org.apache.isis.applib.services.email.EmailNotificationService;
-import org.apache.isis.applib.services.email.events.EmailRegistrationEvent;
+import org.apache.isis.applib.services.email.EmailService;
+import org.apache.isis.applib.services.userreg.EmailNotificationService;
+import org.apache.isis.applib.services.userreg.events.EmailRegistrationEvent;
 import org.apache.isis.viewer.wicket.ui.components.widgets.bootstrap.FormGroup;
 import org.apache.isis.viewer.wicket.ui.pages.accmngt.AccountConfirmationMap;
 import org.apache.isis.viewer.wicket.ui.pages.accmngt.EmailAvailableValidator;
@@ -81,15 +82,16 @@ public class RegistrationFormPanel extends Panel {
                 String confirmationUrl = createUrl(email);
 
                 /**
-                 * We have to init() the service here because the Isis runtime is not available to us
+                 * We have to init() the services here because the Isis runtime is not available to us
                  * (guice will have instantiated a new instance of the service).
                  *
                  * We do it this way just so that the programming model for the EmailService is similar to regular Isis-managed services.
                  */
+                emailNotificationService.init();
                 emailService.init();
 
                 final EmailRegistrationEvent emailRegistrationEvent = new EmailRegistrationEvent(email, confirmationUrl, applicationName);
-                boolean emailSent = emailService.send(emailRegistrationEvent);
+                boolean emailSent = emailNotificationService.send(emailRegistrationEvent);
                 if (emailSent) {
                     Map<String, String> map = new HashMap<>();
                     map.put("email", email);
@@ -120,7 +122,9 @@ public class RegistrationFormPanel extends Panel {
     }
 
     @Inject
-    private EmailNotificationService emailService;
+    private EmailNotificationService emailNotificationService;
+    @Inject
+    private EmailService emailService;
 
     @com.google.inject.Inject
     @Named("applicationName")

http://git-wip-us.apache.org/repos/asf/isis/blob/65e998de/core/applib/src/main/java/org/apache/isis/applib/services/email/EmailNotificationService.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/email/EmailNotificationService.java b/core/applib/src/main/java/org/apache/isis/applib/services/email/EmailNotificationService.java
deleted file mode 100644
index 3ef38b8..0000000
--- a/core/applib/src/main/java/org/apache/isis/applib/services/email/EmailNotificationService.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.apache.isis.applib.services.email;
-
-import java.io.Serializable;
-import javax.annotation.PostConstruct;
-import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.services.email.events.EmailRegistrationEvent;
-import org.apache.isis.applib.services.email.events.PasswordResetEvent;
-
-/**
- * TODO ISIS-987 Javadoc
- */
-public interface EmailNotificationService extends Serializable {
-
-    @PostConstruct
-    @Programmatic
-    public void init() ;
-
-    @Programmatic
-    boolean send(EmailRegistrationEvent emailRegistrationEvent);
-
-    @Programmatic
-    boolean send(PasswordResetEvent emailRegistrationEvent);
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/65e998de/core/applib/src/main/java/org/apache/isis/applib/services/email/EmailService.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/email/EmailService.java b/core/applib/src/main/java/org/apache/isis/applib/services/email/EmailService.java
new file mode 100644
index 0000000..3f992e2
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/email/EmailService.java
@@ -0,0 +1,19 @@
+package org.apache.isis.applib.services.email;
+
+import java.io.Serializable;
+import java.util.List;
+import javax.annotation.PostConstruct;
+import org.apache.isis.applib.annotation.Programmatic;
+
+/**
+ * TODO ISIS-987 Javadoc
+ */
+public interface EmailService extends Serializable {
+
+    @PostConstruct
+    @Programmatic
+    public void init() ;
+
+    @Programmatic
+    boolean send(List<String> to, List<String> cc, List<String> bcc, String subject, String body);
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/65e998de/core/applib/src/main/java/org/apache/isis/applib/services/email/events/EmailEventAbstract.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/email/events/EmailEventAbstract.java b/core/applib/src/main/java/org/apache/isis/applib/services/email/events/EmailEventAbstract.java
deleted file mode 100644
index 36949aa..0000000
--- a/core/applib/src/main/java/org/apache/isis/applib/services/email/events/EmailEventAbstract.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.apache.isis.applib.services.email.events;
-
-/**
- * An event send to all services interested in user registration
- */
-public abstract class EmailEventAbstract {
-
-    private final String email;
-    private final String confirmationUrl;
-    private final String applicationName;
-
-    public EmailEventAbstract(
-            final String email,
-            final String confirmationUrl,
-            final String applicationName) {
-        this.email = email;
-        this.confirmationUrl = confirmationUrl;
-        this.applicationName = applicationName;
-    }
-
-    public String getEmail() {
-        return email;
-    }
-
-    public String getConfirmationUrl() {
-        return confirmationUrl;
-    }
-
-    public String getApplicationName() {
-        return applicationName;
-    }
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/65e998de/core/applib/src/main/java/org/apache/isis/applib/services/email/events/EmailRegistrationEvent.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/email/events/EmailRegistrationEvent.java b/core/applib/src/main/java/org/apache/isis/applib/services/email/events/EmailRegistrationEvent.java
deleted file mode 100644
index fb430b8..0000000
--- a/core/applib/src/main/java/org/apache/isis/applib/services/email/events/EmailRegistrationEvent.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.apache.isis.applib.services.email.events;
-
-/**
- * An event send to all services interested in user registration
- */
-public class EmailRegistrationEvent extends EmailEventAbstract {
-    
-    public EmailRegistrationEvent(
-            final String email,
-            final String confirmationUrl,
-            final String applicationName) {
-        super(email, confirmationUrl, applicationName);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/65e998de/core/applib/src/main/java/org/apache/isis/applib/services/email/events/PasswordResetEvent.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/email/events/PasswordResetEvent.java b/core/applib/src/main/java/org/apache/isis/applib/services/email/events/PasswordResetEvent.java
deleted file mode 100644
index 3a09b4a..0000000
--- a/core/applib/src/main/java/org/apache/isis/applib/services/email/events/PasswordResetEvent.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.apache.isis.applib.services.email.events;
-
-/**
- * An event send to all services interested in user password reset
- */
-public class PasswordResetEvent extends EmailEventAbstract {
-
-    public PasswordResetEvent(
-            final String email,
-            final String confirmationUrl,
-            final String applicationName) {
-        super(email, confirmationUrl, applicationName);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/65e998de/core/applib/src/main/java/org/apache/isis/applib/services/userreg/EmailNotificationService.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/userreg/EmailNotificationService.java b/core/applib/src/main/java/org/apache/isis/applib/services/userreg/EmailNotificationService.java
new file mode 100644
index 0000000..b10c982
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/userreg/EmailNotificationService.java
@@ -0,0 +1,23 @@
+package org.apache.isis.applib.services.userreg;
+
+import java.io.Serializable;
+import javax.annotation.PostConstruct;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.services.userreg.events.EmailRegistrationEvent;
+import org.apache.isis.applib.services.userreg.events.PasswordResetEvent;
+
+/**
+ * TODO ISIS-987 Javadoc
+ */
+public interface EmailNotificationService extends Serializable {
+
+    @PostConstruct
+    @Programmatic
+    public void init() ;
+
+    @Programmatic
+    boolean send(EmailRegistrationEvent emailRegistrationEvent);
+
+    @Programmatic
+    boolean send(PasswordResetEvent emailRegistrationEvent);
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/65e998de/core/applib/src/main/java/org/apache/isis/applib/services/userreg/events/EmailEventAbstract.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/userreg/events/EmailEventAbstract.java b/core/applib/src/main/java/org/apache/isis/applib/services/userreg/events/EmailEventAbstract.java
new file mode 100644
index 0000000..1fa3d04
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/userreg/events/EmailEventAbstract.java
@@ -0,0 +1,32 @@
+package org.apache.isis.applib.services.userreg.events;
+
+/**
+ * An event send to all services interested in user registration
+ */
+public abstract class EmailEventAbstract {
+
+    private final String email;
+    private final String confirmationUrl;
+    private final String applicationName;
+
+    public EmailEventAbstract(
+            final String email,
+            final String confirmationUrl,
+            final String applicationName) {
+        this.email = email;
+        this.confirmationUrl = confirmationUrl;
+        this.applicationName = applicationName;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public String getConfirmationUrl() {
+        return confirmationUrl;
+    }
+
+    public String getApplicationName() {
+        return applicationName;
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/65e998de/core/applib/src/main/java/org/apache/isis/applib/services/userreg/events/EmailRegistrationEvent.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/userreg/events/EmailRegistrationEvent.java b/core/applib/src/main/java/org/apache/isis/applib/services/userreg/events/EmailRegistrationEvent.java
new file mode 100644
index 0000000..76472e2
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/userreg/events/EmailRegistrationEvent.java
@@ -0,0 +1,15 @@
+package org.apache.isis.applib.services.userreg.events;
+
+/**
+ * An event send to all services interested in user registration
+ */
+public class EmailRegistrationEvent extends EmailEventAbstract {
+    
+    public EmailRegistrationEvent(
+            final String email,
+            final String confirmationUrl,
+            final String applicationName) {
+        super(email, confirmationUrl, applicationName);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/65e998de/core/applib/src/main/java/org/apache/isis/applib/services/userreg/events/PasswordResetEvent.java
----------------------------------------------------------------------
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/userreg/events/PasswordResetEvent.java b/core/applib/src/main/java/org/apache/isis/applib/services/userreg/events/PasswordResetEvent.java
new file mode 100644
index 0000000..3f7a485
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/userreg/events/PasswordResetEvent.java
@@ -0,0 +1,15 @@
+package org.apache.isis.applib.services.userreg.events;
+
+/**
+ * An event send to all services interested in user password reset
+ */
+public class PasswordResetEvent extends EmailEventAbstract {
+
+    public PasswordResetEvent(
+            final String email,
+            final String confirmationUrl,
+            final String applicationName) {
+        super(email, confirmationUrl, applicationName);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/65e998de/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/EmailNotificationServiceDefault.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/EmailNotificationServiceDefault.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/EmailNotificationServiceDefault.java
index 1c31780..32b9985 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/EmailNotificationServiceDefault.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/EmailNotificationServiceDefault.java
@@ -2,24 +2,21 @@ package org.apache.isis.core.runtime.services.email;
 
 import java.io.IOException;
 import java.net.URL;
-import java.util.Properties;
+import java.util.Collections;
+import java.util.List;
 import java.util.regex.Pattern;
 import javax.annotation.PostConstruct;
+import javax.inject.Inject;
 import com.google.common.base.Charsets;
-import com.google.common.base.Strings;
 import com.google.common.io.Resources;
-import org.apache.commons.mail.DefaultAuthenticator;
-import org.apache.commons.mail.Email;
-import org.apache.commons.mail.EmailException;
-import org.apache.commons.mail.HtmlEmail;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.services.email.EmailNotificationService;
-import org.apache.isis.applib.services.email.events.EmailEventAbstract;
-import org.apache.isis.applib.services.email.events.EmailRegistrationEvent;
-import org.apache.isis.applib.services.email.events.PasswordResetEvent;
+import org.apache.isis.applib.services.userreg.EmailNotificationService;
+import org.apache.isis.applib.services.userreg.events.EmailEventAbstract;
+import org.apache.isis.applib.services.userreg.events.EmailRegistrationEvent;
+import org.apache.isis.applib.services.userreg.events.PasswordResetEvent;
 import org.apache.isis.core.commons.config.IsisConfiguration;
 import org.apache.isis.core.runtime.system.context.IsisContext;
 
@@ -29,6 +26,7 @@ import static java.util.regex.Pattern.quote;
 /**
  * A service that sends email notifications when specific events occur
  */
+@com.google.inject.Singleton // necessary because is registered in and injected by google guice
 @DomainService
 public class EmailNotificationServiceDefault implements EmailNotificationService {
 
@@ -38,19 +36,10 @@ public class EmailNotificationServiceDefault implements EmailNotificationService
     private static final Pattern CONFIRMATION_URL_PATTERN = compile(quote("${confirmationUrl}"));
     private static final Pattern APPLICATION_NAME_PATTERN = compile(quote("${applicationName}"));
 
-    private static final String ISIS_NOTIFICATION_EMAIL_SENDER_ADDRESS = "isis.notification.email.sender.address";
-    private static final String ISIS_NOTIFICATION_EMAIL_SENDER_PASSWORD = "isis.notification.email.sender.password";
-    private static final String ISIS_NOTIFICATION_EMAIL_SENDER_HOSTNAME = "isis.notification.email.sender.hostname";
-    private static final String ISIS_NOTIFICATION_EMAIL_SENDER_HOSTNAME_DEFAULT = "smtp.gmail.com";
-    private static final String ISIS_NOTIFICATION_EMAIL_PORT = "isis.notification.email.port";
-    private static final String ISIS_NOTIFICATION_EMAIL_TLS_ENABLED = "isis.notification.email.tls.enabled";
-
     private String passwordResetTemplate;
     private String emailVerificationTemplate;
-    private String senderEmailAddress;
-    private String senderEmailPassword;
-    private Integer senderEmailPort;
 
+    private boolean initialized;
 
     /**
      * Loads responsive email templates borrowed from http://zurb.com/ink/templates.php (Basic)
@@ -59,13 +48,14 @@ public class EmailNotificationServiceDefault implements EmailNotificationService
     @Programmatic
     public void init() {
 
+        if(initialized) {
+            return;
+        }
+
         emailVerificationTemplate = loadResource("EmailVerificationTemplate.html");
         passwordResetTemplate = loadResource("PasswordResetTemplate.html");
 
-        senderEmailAddress = getSenderEmailAddress();
-        senderEmailPassword = getSenderEmailPassword();
-
-        senderEmailPort = getSenderEmailPort();
+        initialized = true;
     }
 
     protected String loadResource(final String resourceName) {
@@ -95,51 +85,7 @@ public class EmailNotificationServiceDefault implements EmailNotificationService
         final String subject = buildSubject(emailEvent);
         final String to = emailEvent.getEmail();
 
-        return send(subject, to, body);
-    }
-
-    private boolean send(final String subject, final String to, final String body) {
-        try {
-
-            final Email email = new HtmlEmail();
-            email.setAuthenticator(new DefaultAuthenticator(senderEmailAddress, senderEmailPassword));
-            email.setHostName(getSenderEmailHostName());
-            email.setSmtpPort(senderEmailPort);
-            email.setStartTLSEnabled(getSenderEmailTlsEnabled());
-
-            final Properties properties = email.getMailSession().getProperties();
-
-            // TODO ISIS-987: check whether all these are required and extract as configuration settings
-            properties.put("mail.smtps.auth", "true");
-            properties.put("mail.debug", "true");
-            properties.put("mail.smtps.port", "" + senderEmailPort);
-            properties.put("mail.smtps.socketFactory.port", "" + senderEmailPort);
-            properties.put("mail.smtps.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
-            properties.put("mail.smtps.socketFactory.fallback", "false");
-            properties.put("mail.smtp.starttls.enable", "" + getSenderEmailTlsEnabled());
-
-            email.setFrom(senderEmailAddress);
-
-            email.setSubject(subject);
-            email.setMsg(body);
-
-            email.addTo(to);
-            email.send();
-
-        } catch (EmailException ex) {
-            LOG.error("An error occurred while trying to send an email about user email verification", ex);
-            return false;
-        }
-
-        return true;
-    }
-
-    private String replace(final String template, final EmailEventAbstract emailEvent) {
-        String message = template;
-        message = EMAIL_PATTERN.matcher(message).replaceFirst(emailEvent.getEmail());
-        message = CONFIRMATION_URL_PATTERN.matcher(message).replaceFirst(emailEvent.getConfirmationUrl());
-        message = APPLICATION_NAME_PATTERN.matcher(message).replaceAll(emailEvent.getApplicationName());
-        return message;
+        return send(to, subject, body);
     }
 
     protected String buildSubject(final EmailEventAbstract emailEvent) {
@@ -152,37 +98,27 @@ public class EmailNotificationServiceDefault implements EmailNotificationService
         return subject;
     }
 
-    protected String getSenderEmailAddress() {
-        return getConfigurationPropertyElseThrow(ISIS_NOTIFICATION_EMAIL_SENDER_ADDRESS);
-    }
-
-    protected String getSenderEmailPassword() {
-        return getConfigurationPropertyElseThrow(ISIS_NOTIFICATION_EMAIL_SENDER_PASSWORD);
-    }
-
-    protected String getSenderEmailHostName() {
-        return getConfiguration().getString(ISIS_NOTIFICATION_EMAIL_SENDER_HOSTNAME, ISIS_NOTIFICATION_EMAIL_SENDER_HOSTNAME_DEFAULT);
-    }
+    protected boolean send(final String to, final String subject, final String body) {
 
-    protected Integer getSenderEmailPort() {
-        return getConfiguration().getInteger(ISIS_NOTIFICATION_EMAIL_PORT, 587);
+        final List<String> toList = Collections.singletonList(to);
+        final List<String> cc = Collections.emptyList();
+        final List<String> bcc = Collections.emptyList();
+        return emailService.send(toList, cc, bcc, subject, body);
     }
 
-    protected Boolean getSenderEmailTlsEnabled() {
-        return getConfiguration().getBoolean(ISIS_NOTIFICATION_EMAIL_TLS_ENABLED, true);
+    protected String replace(final String template, final EmailEventAbstract emailEvent) {
+        String message = template;
+        message = EMAIL_PATTERN.matcher(message).replaceFirst(emailEvent.getEmail());
+        message = CONFIRMATION_URL_PATTERN.matcher(message).replaceFirst(emailEvent.getConfirmationUrl());
+        message = APPLICATION_NAME_PATTERN.matcher(message).replaceAll(emailEvent.getApplicationName());
+        return message;
     }
 
     protected IsisConfiguration getConfiguration() {
         return IsisContext.getConfiguration();
     }
 
-    private String getConfigurationPropertyElseThrow(final String configProperty) {
-        final String configuredValue = getConfiguration().getString(configProperty);
-        if(Strings.isNullOrEmpty(configuredValue)) {
-            throw new IllegalStateException(configProperty + " not specified");
-        }
-        return configuredValue;
-    }
-
+    @Inject
+    private EmailServiceDefault emailService;
 
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/65e998de/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/EmailServiceDefault.java
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/EmailServiceDefault.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/EmailServiceDefault.java
new file mode 100644
index 0000000..0a89ee0
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/EmailServiceDefault.java
@@ -0,0 +1,174 @@
+package org.apache.isis.core.runtime.services.email;
+
+import java.util.List;
+import java.util.Properties;
+import java.util.regex.Pattern;
+import javax.annotation.PostConstruct;
+import com.google.common.base.Strings;
+import org.apache.commons.mail.DefaultAuthenticator;
+import org.apache.commons.mail.Email;
+import org.apache.commons.mail.EmailException;
+import org.apache.commons.mail.HtmlEmail;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.services.email.EmailService;
+import org.apache.isis.applib.services.userreg.events.EmailEventAbstract;
+import org.apache.isis.applib.services.userreg.events.EmailRegistrationEvent;
+import org.apache.isis.applib.services.userreg.events.PasswordResetEvent;
+import org.apache.isis.core.commons.config.IsisConfiguration;
+import org.apache.isis.core.runtime.system.context.IsisContext;
+
+import static java.util.regex.Pattern.compile;
+import static java.util.regex.Pattern.quote;
+
+/**
+ * A service that sends email notifications when specific events occur
+ */
+@com.google.inject.Singleton // necessary because is registered in and injected by google guice
+@DomainService
+public class EmailServiceDefault implements EmailService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(EmailServiceDefault.class);
+
+    private static final Pattern EMAIL_PATTERN = compile(quote("${email}"));
+    private static final Pattern CONFIRMATION_URL_PATTERN = compile(quote("${confirmationUrl}"));
+    private static final Pattern APPLICATION_NAME_PATTERN = compile(quote("${applicationName}"));
+
+    private static final String ISIS_SERVICE_EMAIL_SENDER_ADDRESS = "isis.service.email.sender.address";
+    private static final String ISIS_SERVICE_EMAIL_SENDER_PASSWORD = "isis.service.email.sender.password";
+    private static final String ISIS_SERVICE_EMAIL_SENDER_HOSTNAME = "isis.service.email.sender.hostname";
+    private static final String ISIS_SERVICE_EMAIL_SENDER_HOSTNAME_DEFAULT = "smtp.gmail.com";
+    private static final String ISIS_SERVICE_EMAIL_PORT = "isis.service.email.port";
+    private static final String ISIS_SERVICE_EMAIL_TLS_ENABLED = "isis.service.email.tls.enabled";
+
+    private String senderEmailAddress;
+    private String senderEmailPassword;
+    private Integer senderEmailPort;
+
+    private boolean initialized;
+
+    /**
+     * Loads responsive email templates borrowed from http://zurb.com/ink/templates.php (Basic)
+     */
+    @PostConstruct
+    @Programmatic
+    public void init() {
+
+        if(initialized) {
+            return;
+        }
+
+        senderEmailAddress = getSenderEmailAddress();
+        senderEmailPassword = getSenderEmailPassword();
+
+        senderEmailPort = getSenderEmailPort();
+
+        initialized = true;
+    }
+
+
+    @Override
+    public boolean send(final List<String> toList, final List<String> ccList, final List<String> bccList, final String subject, final String body) {
+
+        try {
+
+            final Email email = new HtmlEmail();
+            email.setAuthenticator(new DefaultAuthenticator(senderEmailAddress, senderEmailPassword));
+            email.setHostName(getSenderEmailHostName());
+            email.setSmtpPort(senderEmailPort);
+            email.setStartTLSEnabled(getSenderEmailTlsEnabled());
+
+            final Properties properties = email.getMailSession().getProperties();
+
+            // TODO ISIS-987: check whether all these are required and extract as configuration settings
+            properties.put("mail.smtps.auth", "true");
+            properties.put("mail.debug", "true");
+            properties.put("mail.smtps.port", "" + senderEmailPort);
+            properties.put("mail.smtps.socketFactory.port", "" + senderEmailPort);
+            properties.put("mail.smtps.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
+            properties.put("mail.smtps.socketFactory.fallback", "false");
+            properties.put("mail.smtp.starttls.enable", "" + getSenderEmailTlsEnabled());
+
+            email.setFrom(senderEmailAddress);
+
+            email.setSubject(subject);
+            email.setMsg(body);
+
+            if(notEmpty(toList)) {
+                email.addTo(toList.toArray(new String[toList.size()]));
+            }
+            if(notEmpty(ccList)) {
+                email.addCc(ccList.toArray(new String[ccList.size()]));
+            }
+            if(notEmpty(bccList)) {
+                email.addBcc(bccList.toArray(new String[bccList.size()]));
+            }
+
+            email.send();
+
+        } catch (EmailException ex) {
+            LOG.error("An error occurred while trying to send an email about user email verification", ex);
+            return false;
+        }
+
+        return true;
+    }
+
+    protected boolean notEmpty(final List<String> toList) {
+        return toList != null && !toList.isEmpty();
+    }
+
+    private String replace(final String template, final EmailEventAbstract emailEvent) {
+        String message = template;
+        message = EMAIL_PATTERN.matcher(message).replaceFirst(emailEvent.getEmail());
+        message = CONFIRMATION_URL_PATTERN.matcher(message).replaceFirst(emailEvent.getConfirmationUrl());
+        message = APPLICATION_NAME_PATTERN.matcher(message).replaceAll(emailEvent.getApplicationName());
+        return message;
+    }
+
+    protected String buildSubject(final EmailEventAbstract emailEvent) {
+        String subject = "["+emailEvent.getApplicationName()+"]";
+        if (emailEvent instanceof EmailRegistrationEvent) {
+            subject += " Please confirm your identity";
+        } else if (emailEvent instanceof PasswordResetEvent) {
+            subject += " Password reset request";
+        }
+        return subject;
+    }
+
+    protected String getSenderEmailAddress() {
+        return getConfigurationPropertyElseThrow(ISIS_SERVICE_EMAIL_SENDER_ADDRESS);
+    }
+
+    protected String getSenderEmailPassword() {
+        return getConfigurationPropertyElseThrow(ISIS_SERVICE_EMAIL_SENDER_PASSWORD);
+    }
+
+    protected String getSenderEmailHostName() {
+        return getConfiguration().getString(ISIS_SERVICE_EMAIL_SENDER_HOSTNAME, ISIS_SERVICE_EMAIL_SENDER_HOSTNAME_DEFAULT);
+    }
+
+    protected Integer getSenderEmailPort() {
+        return getConfiguration().getInteger(ISIS_SERVICE_EMAIL_PORT, 587);
+    }
+
+    protected Boolean getSenderEmailTlsEnabled() {
+        return getConfiguration().getBoolean(ISIS_SERVICE_EMAIL_TLS_ENABLED, true);
+    }
+
+    protected IsisConfiguration getConfiguration() {
+        return IsisContext.getConfiguration();
+    }
+
+    private String getConfigurationPropertyElseThrow(final String configProperty) {
+        final String configuredValue = getConfiguration().getString(configProperty);
+        if(Strings.isNullOrEmpty(configuredValue)) {
+            throw new IllegalStateException(configProperty + " not specified");
+        }
+        return configuredValue;
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/65e998de/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/EmailVerificationTemplate.html
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/EmailVerificationTemplate.html b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/EmailVerificationTemplate.html
index d31ae30..78e168e 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/EmailVerificationTemplate.html
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/EmailVerificationTemplate.html
@@ -774,11 +774,11 @@
 
                                                         <h3>Account creation request.</h3>
 
-                                                        <p>It seems someone has requested creation of an account at <strong>${applicationName}</strong>.  </p>
+                                                        <br>It seems someone has requested creation of an account at <strong>${applicationName}</strong>.
 
-                                                        <p>If this was you then please follow this <a href="${confirmationUrl}">link</a> where you can set specify a username and new password.  </p>
+                                                        <br>If this was you then please follow this <a href="${confirmationUrl}">link</a> where you can set specify a username and new password.
 
-                                                        <p>Otherwise please just ignore this email.</p>
+                                                        <br>Otherwise please just ignore this email.
                                                     </td>
                                                 </tr>
                                             </table>

http://git-wip-us.apache.org/repos/asf/isis/blob/65e998de/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/PasswordResetTemplate.html
----------------------------------------------------------------------
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/PasswordResetTemplate.html b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/PasswordResetTemplate.html
index 4cf3c27..038a867 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/PasswordResetTemplate.html
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/email/PasswordResetTemplate.html
@@ -796,11 +796,11 @@
 
                                                             <h3>Password reset request.</h3>
 
-                                                            <p>It seems someone has requested reset of your password for <strong>${applicationName}</strong>.  </p>
+                                                            <br>It seems someone has requested reset of your password for <strong>${applicationName}</strong>.
 
-                                                            <p>If this was you then please follow this <a href="${confirmationUrl}">link</a> where you can set a new password.  </p>
+                                                            <br>If this was you then please follow this <a href="${confirmationUrl}">link</a> where you can set a new password.
 
-                                                            <p>Otherwise please just ignore this email.</p>
+                                                            <br>Otherwise please just ignore this email.
                                                         </td>
                                                     </tr>
                                                 </table>