You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2016/01/29 15:53:17 UTC

[1/2] syncope git commit: [SYNCOPE-760] Mail templates are now managed entities, not from filesystem anymore

Repository: syncope
Updated Branches:
  refs/heads/master a4a9ef568 -> 2ac6f8d50


http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.jexl3
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.jexl3 b/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.jexl3
deleted file mode 100644
index 0c0428d..0000000
--- a/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.jexl3
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations
-under the License.
-*/
-<html>
-<body>
-<p>Hi,
-a password reset was request for ${user.getUsername()}.</p>
-
-<p>In order to complete this request, you need to visit this 
-<a href="http://localhost:9080/syncope-console/?pwdResetToken=${input.get(0).replaceAll(' ', '%20')}">link</a></p>.
-
-<p>If you did not request this reset, just ignore the present e-mail.</p>
-
-<p>Best regards.</p>
-</body>
-</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.jexl3
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.jexl3 b/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.jexl3
deleted file mode 100644
index 4778884..0000000
--- a/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.jexl3
+++ /dev/null
@@ -1,26 +0,0 @@
-## Licensed to the Apache Software Foundation (ASF) under one
-## or more contributor license agreements.  See the NOTICE file
-## distributed with this work for additional information
-## regarding copyright ownership.  The ASF licenses this file
-## to you under the Apache License, Version 2.0 (the
-## "License"); you may not use this file except in compliance
-## with the License.  You may obtain a copy of the License at
-##
-##   http://www.apache.org/licenses/LICENSE-2.0
-##
-## Unless required by applicable law or agreed to in writing,
-## software distributed under the License is distributed on an
-## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-## KIND, either express or implied.  See the License for the
-## specific language governing permissions and limitations
-## under the License.
-Hi,
-a password reset was request for ${user.getUsername()}.
-
-In order to complete this request, you need to visit this link:
-
-http://localhost:9080/syncope-console/?pwdResetToken=${input.get(0).replaceAll(' ', '%20')}
-
-If you did not request this reset, just ignore the present e-mail.
-
-Best regards.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/provisioning-java/src/main/resources/provisioningContext.xml
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/provisioningContext.xml b/core/provisioning-java/src/main/resources/provisioningContext.xml
index 8f4dee9..ae1bb5e 100644
--- a/core/provisioning-java/src/main/resources/provisioningContext.xml
+++ b/core/provisioning-java/src/main/resources/provisioningContext.xml
@@ -118,11 +118,6 @@ under the License.
     <constructor-arg value="5000"/>
   </bean>
 
-  <bean id="mailTemplateResourceLoader" class="org.apache.syncope.core.misc.spring.ResourceWithFallbackLoader">
-    <property name="primary" value="file:${templates.directory}/"/>
-    <property name="fallback" value="classpath:"/>
-  </bean>
-
   <bean id="connIdBundleManager" class="org.apache.syncope.core.provisioning.java.ConnIdBundleManagerImpl">
     <property name="stringLocations" value="${connid.locations}"/>
   </bean>

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/MailTemplateTest.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/MailTemplateTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/MailTemplateTest.java
index d123792..3697d04 100644
--- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/MailTemplateTest.java
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/MailTemplateTest.java
@@ -30,32 +30,27 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
-import javax.annotation.Resource;
-import org.apache.commons.io.IOUtils;
 import org.apache.commons.jexl3.MapContext;
 import org.apache.commons.lang3.SerializationUtils;
 import org.apache.syncope.common.lib.to.AttrTO;
 import org.apache.syncope.common.lib.to.MembershipTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.core.misc.jexl.JexlUtils;
-import org.apache.syncope.core.misc.spring.ResourceWithFallbackLoader;
-import org.apache.syncope.core.provisioning.java.notification.NotificationManagerImpl;
+import org.apache.syncope.core.persistence.api.dao.MailTemplateDAO;
 import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 
 @Transactional("Master")
 public class MailTemplateTest extends AbstractTest {
 
-    @Resource(name = "mailTemplateResourceLoader")
-    private ResourceWithFallbackLoader mailTemplateResourceLoader;
-
-    private String evaluate(final String templateLocation, final Map<String, Object> jexlVars) throws IOException {
-        org.springframework.core.io.Resource templateResource =
-                mailTemplateResourceLoader.getResource(templateLocation);
+    @Autowired
+    private MailTemplateDAO mailTemplateDAO;
 
+    private String evaluate(final String template, final Map<String, Object> jexlVars) {
         StringWriter writer = new StringWriter();
         JexlUtils.newJxltEngine().
-                createTemplate(IOUtils.toString(templateResource.getInputStream())).
+                createTemplate(template).
                 evaluate(new MapContext(jexlVars), writer);
         return writer.toString();
     }
@@ -63,9 +58,7 @@ public class MailTemplateTest extends AbstractTest {
     @Test
     public void confirmPasswordReset() throws IOException {
         String htmlBody = evaluate(
-                NotificationManagerImpl.MAIL_TEMPLATES
-                + "confirmPasswordReset"
-                + NotificationManagerImpl.MAIL_TEMPLATE_HTML_SUFFIX,
+                mailTemplateDAO.find("confirmPasswordReset").getHTMLTemplate(),
                 new HashMap<String, Object>());
 
         assertNotNull(htmlBody);
@@ -86,9 +79,7 @@ public class MailTemplateTest extends AbstractTest {
         ctx.put("input", input);
 
         String htmlBody = evaluate(
-                NotificationManagerImpl.MAIL_TEMPLATES
-                + "requestPasswordReset"
-                + NotificationManagerImpl.MAIL_TEMPLATE_HTML_SUFFIX,
+                mailTemplateDAO.find("requestPasswordReset").getHTMLTemplate(),
                 ctx);
 
         assertNotNull(htmlBody);
@@ -122,12 +113,12 @@ public class MailTemplateTest extends AbstractTest {
         ctx.put("recipients", Collections.singletonList(recipient));
 
         String htmlBody = evaluate(
-                NotificationManagerImpl.MAIL_TEMPLATES
-                + "optin"
-                + NotificationManagerImpl.MAIL_TEMPLATE_HTML_SUFFIX,
+                mailTemplateDAO.find("optin").getHTMLTemplate(),
                 ctx);
 
         assertNotNull(htmlBody);
+        System.out.println("AAAAAAAAAAAAA\n" + htmlBody);
+        
         assertTrue(htmlBody.contains("Hi John Doe,"));
         assertTrue(htmlBody.contains("Your email address is john.doe@syncope.apache.org."));
         assertTrue(htmlBody.contains("<li>another@syncope.apache.org</li>"));

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/MailTemplateServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/MailTemplateServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/MailTemplateServiceImpl.java
new file mode 100644
index 0000000..967b2f0
--- /dev/null
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/MailTemplateServiceImpl.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.rest.cxf.service;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.List;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.StreamingOutput;
+import org.apache.commons.io.IOUtils;
+import org.apache.syncope.common.lib.to.MailTemplateTO;
+import org.apache.syncope.common.lib.types.MailTemplateFormat;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.MailTemplateService;
+import org.apache.syncope.core.logic.MailTemplateLogic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class MailTemplateServiceImpl extends AbstractServiceImpl implements MailTemplateService {
+
+    @Autowired
+    private MailTemplateLogic logic;
+
+    @Override
+    public Response create(final MailTemplateTO mailTemplateTO) {
+        MailTemplateTO created = logic.create(mailTemplateTO.getKey());
+        URI location = uriInfo.getAbsolutePathBuilder().path(String.valueOf(created.getKey())).build();
+        return Response.created(location).
+                header(RESTHeaders.RESOURCE_KEY, created.getKey()).
+                build();
+    }
+
+    @Override
+    public MailTemplateTO read(final String key) {
+        return logic.read(key);
+    }
+
+    @Override
+    public List<MailTemplateTO> list() {
+        return logic.list();
+    }
+
+    @Override
+    public void delete(final String key) {
+        logic.delete(key);
+    }
+
+    @Override
+    public Response getFormat(final String key, final MailTemplateFormat format) {
+        final String template = logic.getFormat(key, format);
+        StreamingOutput sout = new StreamingOutput() {
+
+            @Override
+            public void write(final OutputStream os) throws IOException {
+                os.write(template.getBytes());
+            }
+        };
+        return Response.ok(sout).
+                type(format.getMediaType()).
+                build();
+    }
+
+    @Override
+    public void setFormat(final String key, final MailTemplateFormat format, final InputStream templateIn) {
+        try {
+            logic.setFormat(key, format, IOUtils.toString(templateIn));
+        } catch (final IOException e) {
+            LOG.error("While setting format {} for mail template {}", format, key, e);
+            throw new InternalServerErrorException("Could not read entity", e);
+        }
+    }
+
+    @Override
+    public void removeFormat(final String key, final MailTemplateFormat format) {
+        logic.setFormat(key, format, null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java
index 82c25db..e80c611 100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java
@@ -81,10 +81,9 @@ public class ReportServiceImpl extends AbstractServiceImpl implements ReportServ
                 logic.exportExecutionResult(os, reportExec, format);
             }
         };
-        String disposition = "attachment; filename=" + reportExec.getReport().getName() + "." + format.name().
-                toLowerCase();
         return Response.ok(sout).
-                header(HttpHeaders.CONTENT_DISPOSITION, disposition).
+                header(HttpHeaders.CONTENT_DISPOSITION,
+                        "attachment; filename=" + reportExec.getReport().getName() + "." + format.name().toLowerCase()).
                 build();
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/deb/core/pom.xml
----------------------------------------------------------------------
diff --git a/deb/core/pom.xml b/deb/core/pom.xml
index 8be896ba..65e5031 100644
--- a/deb/core/pom.xml
+++ b/deb/core/pom.xml
@@ -140,7 +140,6 @@ under the License.
         <includes>
           <include>mail.properties</include>
           <include>connid.properties</include>
-          <include>mailTemplates/*.jexl3</include>
         </includes>
         <targetPath>${project.build.directory}/etc</targetPath>
         <filtering>true</filtering>

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/deb/core/src/deb/control/conffiles
----------------------------------------------------------------------
diff --git a/deb/core/src/deb/control/conffiles b/deb/core/src/deb/control/conffiles
index 38d5f12..ae391c2 100644
--- a/deb/core/src/deb/control/conffiles
+++ b/deb/core/src/deb/control/conffiles
@@ -12,9 +12,3 @@
 /etc/apache-syncope/userRoutes.xml
 /etc/apache-syncope/userWorkflow.bpmn20.xml
 /etc/apache-syncope/workflow.properties
-/etc/apache-syncope/mailTemplates/confirmPasswordReset.html.jexl3
-/etc/apache-syncope/mailTemplates/confirmPasswordReset.txt.jexl3
-/etc/apache-syncope/mailTemplates/optin.html.jexl3
-/etc/apache-syncope/mailTemplates/optin.txt.jexl3
-/etc/apache-syncope/mailTemplates/requestPasswordReset.html.jexl3
-/etc/apache-syncope/mailTemplates/requestPasswordReset.txt.jexl3

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/fit/core-reference/src/main/resources/mail.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/mail.properties b/fit/core-reference/src/main/resources/mail.properties
index 66f2e90..e8b9a5f 100644
--- a/fit/core-reference/src/main/resources/mail.properties
+++ b/fit/core-reference/src/main/resources/mail.properties
@@ -14,7 +14,6 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-templates.directory=${conf.directory}
 smtpHost=localhost
 smtpPort=2525
 smtpUser=

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index 27db9db..b7af72a 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -67,6 +67,7 @@ import org.apache.syncope.common.rest.api.service.PolicyService;
 import org.apache.syncope.common.rest.api.service.ReportService;
 import org.apache.syncope.common.rest.api.service.ResourceService;
 import org.apache.syncope.common.rest.api.service.GroupService;
+import org.apache.syncope.common.rest.api.service.MailTemplateService;
 import org.apache.syncope.common.rest.api.service.RealmService;
 import org.apache.syncope.common.rest.api.service.RelationshipTypeService;
 import org.apache.syncope.common.rest.api.service.RoleService;
@@ -193,6 +194,8 @@ public abstract class AbstractITCase {
 
     protected static WorkflowService workflowService;
 
+    protected static MailTemplateService mailTemplateService;
+
     protected static NotificationService notificationService;
 
     protected static SchemaService schemaService;
@@ -258,6 +261,7 @@ public abstract class AbstractITCase {
         taskService = adminClient.getService(TaskService.class);
         policyService = adminClient.getService(PolicyService.class);
         workflowService = adminClient.getService(WorkflowService.class);
+        mailTemplateService = adminClient.getService(MailTemplateService.class);
         notificationService = adminClient.getService(NotificationService.class);
         schemaService = adminClient.getService(SchemaService.class);
         securityQuestionService = adminClient.getService(SecurityQuestionService.class);

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MailTemplateITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MailTemplateITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MailTemplateITCase.java
new file mode 100644
index 0000000..9a7f511
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MailTemplateITCase.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.fit.core;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.commons.io.IOUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.MailTemplateTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.MailTemplateFormat;
+import org.apache.syncope.fit.AbstractITCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.JVM)
+public class MailTemplateITCase extends AbstractITCase {
+
+    @Test
+    public void read() {
+        MailTemplateTO mailTemplateTO = mailTemplateService.read("optin");
+        assertNotNull(mailTemplateTO);
+    }
+
+    @Test
+    public void list() {
+        List<MailTemplateTO> mailTemplateTOs = mailTemplateService.list();
+        assertNotNull(mailTemplateTOs);
+        assertFalse(mailTemplateTOs.isEmpty());
+        for (MailTemplateTO instance : mailTemplateTOs) {
+            assertNotNull(instance);
+        }
+    }
+
+    @Test
+    public void crud() throws IOException {
+        final String key = getUUIDString();
+
+        // 1. create (empty) mail template
+        MailTemplateTO mailTemplateTO = new MailTemplateTO();
+        mailTemplateTO.setKey(key);
+
+        Response response = mailTemplateService.create(mailTemplateTO);
+        assertEquals(201, response.getStatus());
+
+        // 2. attempt to read HTML and TEXT -> fail
+        try {
+            mailTemplateService.getFormat(key, MailTemplateFormat.HTML);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+        }
+        try {
+            mailTemplateService.getFormat(key, MailTemplateFormat.TEXT);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+        }
+
+        // 3. set TEXT
+        String textTemplate = "Hi there, I am ${username}.";
+        mailTemplateService.setFormat(
+                key, MailTemplateFormat.TEXT, IOUtils.toInputStream(textTemplate));
+
+        response = mailTemplateService.getFormat(key, MailTemplateFormat.TEXT);
+        assertEquals(200, response.getStatus());
+        assertTrue(response.getMediaType().toString().startsWith(MediaType.TEXT_PLAIN));
+        assertTrue(response.getEntity() instanceof InputStream);
+        assertEquals(textTemplate, IOUtils.toString((InputStream) response.getEntity()));
+
+        // 3. set HTML
+        String htmlTemplate = "<html><body>Hi there, I am ${username}.</body></html>";
+        mailTemplateService.setFormat(
+                key, MailTemplateFormat.HTML, IOUtils.toInputStream(htmlTemplate));
+
+        response = mailTemplateService.getFormat(key, MailTemplateFormat.HTML);
+        assertEquals(200, response.getStatus());
+        assertTrue(response.getMediaType().toString().startsWith(MediaType.TEXT_HTML));
+        assertTrue(response.getEntity() instanceof InputStream);
+        assertEquals(htmlTemplate, IOUtils.toString((InputStream) response.getEntity()));
+
+        // 4. remove HTML
+        mailTemplateService.removeFormat(key, MailTemplateFormat.HTML);
+
+        try {
+            mailTemplateService.getFormat(key, MailTemplateFormat.HTML);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+        }
+
+        response = mailTemplateService.getFormat(key, MailTemplateFormat.TEXT);
+        assertEquals(200, response.getStatus());
+        assertTrue(response.getMediaType().toString().startsWith(MediaType.TEXT_PLAIN));
+        assertTrue(response.getEntity() instanceof InputStream);
+        assertEquals(textTemplate, IOUtils.toString((InputStream) response.getEntity()));
+
+        // 5. remove mail template
+        mailTemplateService.delete(key);
+
+        try {
+            mailTemplateService.read(key);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+        }
+        try {
+            mailTemplateService.getFormat(key, MailTemplateFormat.HTML);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+        }
+        try {
+            mailTemplateService.getFormat(key, MailTemplateFormat.TEXT);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.NotFound, e.getType());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 12f9354..ab6bcdc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -389,7 +389,7 @@ under the License.
     <font-awesome.version>4.5.0</font-awesome.version>
     <ionicons.version>2.0.1</ionicons.version>
     <highlightjs.version>8.7</highlightjs.version>
-    <codemirror.version>5.8</codemirror.version>
+    <codemirror.version>5.11</codemirror.version>
     <jsplumb.version>1.7.5</jsplumb.version>
     
     <wicket.version>7.1.0</wicket.version>


[2/2] syncope git commit: [SYNCOPE-760] Mail templates are now managed entities, not from filesystem anymore

Posted by il...@apache.org.
[SYNCOPE-760] Mail templates are now managed entities, not from filesystem anymore


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/2ac6f8d5
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/2ac6f8d5
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/2ac6f8d5

Branch: refs/heads/master
Commit: 2ac6f8d50daebcc0f1070f77a4df19059cfc2704
Parents: a4a9ef5
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Fri Jan 29 15:53:04 2016 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Fri Jan 29 15:53:04 2016 +0100

----------------------------------------------------------------------
 .../syncope/client/cli/commands/info/Info.java  |   9 -
 .../client/cli/commands/info/InfoCommand.java   |   4 -
 .../cli/commands/info/InfoResultManager.java    |   6 -
 .../console/rest/ConfigurationRestClient.java   |   8 +-
 .../syncope/common/lib/to/MailTemplateTO.java   |  44 +++++
 .../apache/syncope/common/lib/to/SyncopeTO.java |   9 -
 .../common/lib/types/ClientExceptionType.java   |   1 +
 .../common/lib/types/MailTemplateFormat.java    |  40 ++++
 .../common/lib/types/StandardEntitlement.java   |  10 +
 .../rest/api/service/MailTemplateService.java   | 121 ++++++++++++
 .../rest/api/service/NotificationService.java   |   2 +-
 .../common/rest/api/service/ReportService.java  |   3 +-
 .../syncope/core/logic/MailTemplateLogic.java   | 186 +++++++++++++++++++
 .../apache/syncope/core/logic/SyncopeLogic.java |  32 ----
 .../persistence/api/dao/MailTemplateDAO.java    |  33 ++++
 .../persistence/api/dao/NotificationDAO.java    |   3 +
 .../persistence/api/entity/MailTemplate.java    |  32 ++++
 .../persistence/api/entity/Notification.java    |   4 +-
 .../persistence/jpa/dao/JPAMailTemplateDAO.java |  57 ++++++
 .../persistence/jpa/dao/JPANotificationDAO.java |  10 +
 .../persistence/jpa/entity/JPAAnyAbout.java     |   6 +-
 .../jpa/entity/JPAEntityFactory.java            |   3 +
 .../persistence/jpa/entity/JPAMailTemplate.java |  76 ++++++++
 .../persistence/jpa/entity/JPANotification.java |  14 +-
 .../main/resources/domains/MasterContent.xml    |  44 ++++-
 .../persistence/jpa/inner/MailTemplateTest.java |  77 ++++++++
 .../persistence/jpa/inner/NotificationTest.java |  10 +-
 .../test/resources/domains/MasterContent.xml    | 108 ++++++++++-
 .../src/test/resources/domains/TwoContent.xml   |  44 ++++-
 .../java/data/NotificationDataBinderImpl.java   |  10 +-
 .../notification/NotificationManagerImpl.java   |  45 ++---
 .../src/main/resources/mail.properties          |   1 -
 .../confirmPasswordReset.html.jexl3             |  26 ---
 .../confirmPasswordReset.txt.jexl3              |  20 --
 .../resources/mailTemplates/optin.html.jexl3    |  54 ------
 .../resources/mailTemplates/optin.txt.jexl3     |  38 ----
 .../requestPasswordReset.html.jexl3             |  31 ----
 .../requestPasswordReset.txt.jexl3              |  26 ---
 .../src/main/resources/provisioningContext.xml  |   5 -
 .../provisioning/java/MailTemplateTest.java     |  31 ++--
 .../cxf/service/MailTemplateServiceImpl.java    |  97 ++++++++++
 .../rest/cxf/service/ReportServiceImpl.java     |   5 +-
 deb/core/pom.xml                                |   1 -
 deb/core/src/deb/control/conffiles              |   6 -
 .../src/main/resources/mail.properties          |   1 -
 .../org/apache/syncope/fit/AbstractITCase.java  |   4 +
 .../syncope/fit/core/MailTemplateITCase.java    | 146 +++++++++++++++
 pom.xml                                         |   2 +-
 48 files changed, 1184 insertions(+), 361 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/Info.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/Info.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/Info.java
index bdb430f..6f68af4 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/Info.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/Info.java
@@ -128,15 +128,6 @@ public class Info {
         }
     }
 
-    public void mailTemplates() {
-        try {
-            infoResultManager.printMailTemplates(syncopeTO.getMailTemplates());
-        } catch (final Exception ex) {
-            LOG.error("Information error", ex);
-            infoResultManager.genericError(ex.getMessage());
-        }
-    }
-
     public void mappingItemTransformers() {
         try {
             infoResultManager.printMappingItemTransformers(syncopeTO.getMappingItemTransformers());

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/InfoCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/InfoCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/InfoCommand.java
index db2c283..1991fdd 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/InfoCommand.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/InfoCommand.java
@@ -67,9 +67,6 @@ public class InfoCommand extends AbstractCommand {
             case LOGIC_ACTIONS:
                 info.logicActions();
                 break;
-            case MAIL_TEMPLATES:
-                info.mailTemplates();
-                break;
             case MAPPING_ITEM_TRANSFORMERS:
                 info.mappingItemTransformers();
                 break;
@@ -133,7 +130,6 @@ public class InfoCommand extends AbstractCommand {
         CONNID_LOCATION("--connid-locations"),
         RECONCILIATION_FILTER_BUILDERS("--reconciliation-filter-builders"),
         LOGIC_ACTIONS("--logic-actions"),
-        MAIL_TEMPLATES("--mail-templates"),
         MAPPING_ITEM_TRANSFORMERS("--mapping-item-transformers"),
         PASSWORD_RULES("--password-rules"),
         PROPAGATION_ACTIONS("--propagation-actions"),

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/InfoResultManager.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/InfoResultManager.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/InfoResultManager.java
index f5f816f..4494d86 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/InfoResultManager.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/info/InfoResultManager.java
@@ -81,12 +81,6 @@ public class InfoResultManager extends CommonsResultManager {
         }
     }
 
-    public void printMailTemplates(final Collection<String> mailTemplates) {
-        for (final String template : mailTemplates) {
-            genericMessage("Mail template: " + template);
-        }
-    }
-
     public void printMappingItemTransformers(final Collection<String> transformers) {
         for (final String tranformer : transformers) {
             genericMessage("Mapping item tranformer: " + tranformer);

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/client/console/src/main/java/org/apache/syncope/client/console/rest/ConfigurationRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/ConfigurationRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/ConfigurationRestClient.java
index e28a914..dfdd842 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/rest/ConfigurationRestClient.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/ConfigurationRestClient.java
@@ -19,9 +19,7 @@
 package org.apache.syncope.client.console.rest;
 
 import java.util.List;
-import java.util.Set;
 import javax.ws.rs.core.Response;
-import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.console.commons.AttrLayoutType;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.AttrTO;
@@ -31,7 +29,7 @@ public class ConfigurationRestClient extends BaseRestClient {
 
     private static final long serialVersionUID = 7692363064029538722L;
 
-    private SchemaRestClient schemaRestClient = new SchemaRestClient();
+    private final SchemaRestClient schemaRestClient = new SchemaRestClient();
 
     public List<AttrTO> list() {
         final List<AttrTO> attrTOs = getService(ConfigurationService.class).list();
@@ -81,10 +79,6 @@ public class ConfigurationRestClient extends BaseRestClient {
         getService(ConfigurationService.class).delete(key);
     }
 
-    public Set<String> getMailTemplates() {
-        return SyncopeConsoleSession.get().getSyncopeTO().getMailTemplates();
-    }
-
     public Response dbExport() {
         return getService(ConfigurationService.class).export();
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/common/lib/src/main/java/org/apache/syncope/common/lib/to/MailTemplateTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/MailTemplateTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/MailTemplateTO.java
new file mode 100644
index 0000000..b5ea0ca
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/MailTemplateTO.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.lib.to;
+
+import javax.ws.rs.PathParam;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+
+@XmlRootElement(name = "maiLTemplate")
+@XmlType
+public class MailTemplateTO extends AbstractBaseBean implements EntityTO<String> {
+
+    private static final long serialVersionUID = 8389755049666062735L;
+
+    private String key;
+
+    @Override
+    public String getKey() {
+        return key;
+    }
+
+    @PathParam("key")
+    @Override
+    public void setKey(final String key) {
+        this.key = key;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java
index 56e518f..a3c03b6 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java
@@ -89,8 +89,6 @@ public class SyncopeTO extends AbstractBaseBean {
 
     private final Set<String> notificationRecipientsProviders = new HashSet<>();
 
-    private final Set<String> mailTemplates = new HashSet<>();
-
     public String getVersion() {
         return version;
     }
@@ -255,13 +253,6 @@ public class SyncopeTO extends AbstractBaseBean {
         return notificationRecipientsProviders;
     }
 
-    @XmlElementWrapper(name = "mailTemplates")
-    @XmlElement(name = "mailTemplate")
-    @JsonProperty("mailTemplates")
-    public Set<String> getMailTemplates() {
-        return mailTemplates;
-    }
-
     public void setVersion(final String version) {
         this.version = version;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
index dfc5fe2..0fc8a7f 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java
@@ -68,6 +68,7 @@ public enum ClientExceptionType {
     RequiredValuesMissing(Response.Status.BAD_REQUEST),
     RESTValidation(Response.Status.BAD_REQUEST),
     GroupOwnership(Response.Status.BAD_REQUEST),
+    InUseByNotifications(Response.Status.BAD_REQUEST),
     Scheduling(Response.Status.BAD_REQUEST),
     DelegatedAdministration(Response.Status.FORBIDDEN),
     Unknown(Response.Status.BAD_REQUEST),

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/common/lib/src/main/java/org/apache/syncope/common/lib/types/MailTemplateFormat.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/MailTemplateFormat.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/MailTemplateFormat.java
new file mode 100644
index 0000000..fdf8429
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/MailTemplateFormat.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.lib.types;
+
+import javax.ws.rs.core.MediaType;
+import javax.xml.bind.annotation.XmlEnum;
+
+@XmlEnum
+public enum MailTemplateFormat {
+
+    HTML(MediaType.TEXT_HTML_TYPE),
+    TEXT(MediaType.TEXT_PLAIN_TYPE);
+
+    private final MediaType mediaType;
+
+    MailTemplateFormat(final MediaType mediaType) {
+        this.mediaType = mediaType;
+    }
+
+    public MediaType getMediaType() {
+        return mediaType;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
index 01ca830..339990f 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
@@ -178,6 +178,16 @@ public final class StandardEntitlement {
 
     public static final String WORKFLOW_FORM_SUBMIT = "WORKFLOW_FORM_SUBMIT";
 
+    public static final String MAIL_TEMPLATE_LIST = "MAIL_TEMPLATE_LIST";
+
+    public static final String MAIL_TEMPLATE_CREATE = "MAIL_TEMPLATE_CREATE";
+
+    public static final String MAIL_TEMPLATE_READ = "MAIL_TEMPLATE_READ";
+
+    public static final String MAIL_TEMPLATE_UPDATE = "MAIL_TEMPLATE_UPDATE";
+
+    public static final String MAIL_TEMPLATE_DELETE = "MAIL_TEMPLATE_DELETE";
+
     public static final String NOTIFICATION_LIST = "NOTIFICATION_LIST";
 
     public static final String NOTIFICATION_CREATE = "NOTIFICATION_CREATE";

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/MailTemplateService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/MailTemplateService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/MailTemplateService.java
new file mode 100644
index 0000000..d8e747a
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/MailTemplateService.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.rest.api.service;
+
+import java.io.InputStream;
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.to.MailTemplateTO;
+import org.apache.syncope.common.lib.types.MailTemplateFormat;
+
+/**
+ * REST operations for mail templates.
+ */
+@Path("mailTemplates")
+public interface MailTemplateService extends JAXRSService {
+
+    /**
+     * Returns a list of all mail templates.
+     *
+     * @return list of all mail templates.
+     */
+    @GET
+    @Produces({ JAXRSService.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    List<MailTemplateTO> list();
+
+    /**
+     * Creates a new mail template.
+     *
+     * @param mailTemplateTO Creates a new mail template.
+     * @return Response object featuring Location header of created mail template
+     */
+    @POST
+    @Consumes({ JAXRSService.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    Response create(@NotNull MailTemplateTO mailTemplateTO);
+
+    /**
+     * Returns mail template with matching key.
+     *
+     * @param key key of mail template to be read
+     * @return mail template with matching key
+     */
+    @GET
+    @Path("{key}")
+    @Produces({ JAXRSService.APPLICATION_XML, MediaType.APPLICATION_JSON })
+    MailTemplateTO read(@NotNull @PathParam("key") String key);
+
+    /**
+     * Deletes the mail template matching the given key.
+     *
+     * @param key key for mail template to be deleted
+     */
+    @DELETE
+    @Path("{key}")
+    void delete(@NotNull @PathParam("key") String key);
+
+    /**
+     * Gets the template for the given key and format, if available.
+     *
+     * @param key mail template
+     * @param format template format
+     * @return mail template with matching key and format, if available
+     */
+    @GET
+    @Path("{key}/{format}")
+    Response getFormat(
+            @NotNull @PathParam("key") String key,
+            @NotNull @PathParam("format") MailTemplateFormat format);
+
+    /**
+     * Sets the template for the given key and format, if available.
+     *
+     * @param key mail template
+     * @param format template format
+     * @param templateIn template to be set
+     */
+    @PUT
+    @Path("{key}/{format}")
+    void setFormat(
+            @NotNull @PathParam("key") String key,
+            @NotNull @PathParam("format") MailTemplateFormat format,
+            InputStream templateIn);
+
+    /**
+     * Removes the template for the given key and format, if available.
+     *
+     * @param key mail template
+     * @param format template format
+     */
+    @DELETE
+    @Path("{key}/{format}")
+    void removeFormat(
+            @NotNull @PathParam("key") String key,
+            @NotNull @PathParam("format") MailTemplateFormat format);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/NotificationService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/NotificationService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/NotificationService.java
index d67e4a0..daa0400 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/NotificationService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/NotificationService.java
@@ -39,7 +39,7 @@ import org.apache.syncope.common.lib.to.NotificationTO;
 public interface NotificationService extends JAXRSService {
 
     /**
-     * Returns notification with matching id.
+     * Returns notification with matching key.
      *
      * @param key key of notification to be read
      * @return notification with matching key

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
index 87eb28f..c1427b8 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
@@ -138,7 +138,8 @@ public interface ReportService extends JAXRSService {
     @GET
     @Path("executions/{executionKey}/stream")
     @Consumes({ JAXRSService.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    Response exportExecutionResult(@NotNull @PathParam("executionKey") Long executionKey,
+    Response exportExecutionResult(
+            @NotNull @PathParam("executionKey") Long executionKey,
             @QueryParam("format") ReportExecExportFormat fmt);
 
     /**

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/logic/src/main/java/org/apache/syncope/core/logic/MailTemplateLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/MailTemplateLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/MailTemplateLogic.java
new file mode 100644
index 0000000..7e5b120
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/MailTemplateLogic.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.MailTemplateTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.MailTemplateFormat;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.MailTemplateDAO;
+import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.MailTemplate;
+import org.apache.syncope.core.persistence.api.entity.Notification;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+@Component
+public class MailTemplateLogic extends AbstractTransactionalLogic<MailTemplateTO> {
+
+    @Autowired
+    private MailTemplateDAO mailTemplateDAO;
+
+    @Autowired
+    private NotificationDAO notificationDAO;
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    private MailTemplateTO getMailTemplateTO(final String key) {
+        MailTemplateTO mailTemplateTO = new MailTemplateTO();
+        mailTemplateTO.setKey(key);
+        return mailTemplateTO;
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.MAIL_TEMPLATE_READ + "')")
+    public MailTemplateTO read(final String key) {
+        MailTemplate mailTemplate = mailTemplateDAO.find(key);
+        if (mailTemplate == null) {
+            LOG.error("Could not find mail template '" + key + "'");
+
+            throw new NotFoundException(key);
+        }
+
+        return getMailTemplateTO(key);
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.MAIL_TEMPLATE_LIST + "')")
+    public List<MailTemplateTO> list() {
+        return CollectionUtils.collect(mailTemplateDAO.findAll(), new Transformer<MailTemplate, MailTemplateTO>() {
+
+            @Override
+            public MailTemplateTO transform(final MailTemplate input) {
+                return getMailTemplateTO(input.getKey());
+            }
+        }, new ArrayList<MailTemplateTO>());
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.MAIL_TEMPLATE_CREATE + "')")
+    public MailTemplateTO create(final String key) {
+        MailTemplate mailTemplate = entityFactory.newEntity(MailTemplate.class);
+        mailTemplate.setKey(key);
+        mailTemplateDAO.save(mailTemplate);
+
+        return getMailTemplateTO(key);
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.MAIL_TEMPLATE_READ + "')")
+    public String getFormat(final String key, final MailTemplateFormat format) {
+        MailTemplate mailTemplate = mailTemplateDAO.find(key);
+        if (mailTemplate == null) {
+            LOG.error("Could not find mail template '" + key + "'");
+
+            throw new NotFoundException(key);
+        }
+
+        String template = format == MailTemplateFormat.HTML
+                ? mailTemplate.getHTMLTemplate()
+                : mailTemplate.getTextTemplate();
+        if (StringUtils.isBlank(template)) {
+            LOG.error("Could not find mail template '" + key + "' in " + format + " format");
+
+            throw new NotFoundException(key + " in " + format);
+        }
+
+        return template;
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.MAIL_TEMPLATE_UPDATE + "')")
+    public void setFormat(final String key, final MailTemplateFormat format, final String template) {
+        MailTemplate mailTemplate = mailTemplateDAO.find(key);
+        if (mailTemplate == null) {
+            LOG.error("Could not find mail template '" + key + "'");
+
+            throw new NotFoundException(key);
+        }
+
+        if (format == MailTemplateFormat.HTML) {
+            mailTemplate.setHTMLTemplate(template);
+        } else {
+            mailTemplate.setTextTemplate(template);
+        }
+
+        mailTemplateDAO.save(mailTemplate);
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.MAIL_TEMPLATE_DELETE + "')")
+    public MailTemplateTO delete(final String key) {
+        MailTemplate mailTemplate = mailTemplateDAO.find(key);
+        if (mailTemplate == null) {
+            LOG.error("Could not find mail template '" + key + "'");
+
+            throw new NotFoundException(key);
+        }
+
+        List<Notification> notifications = notificationDAO.findByTemplate(mailTemplate);
+        if (!notifications.isEmpty()) {
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InUseByNotifications);
+            sce.getElements().addAll(CollectionUtils.collect(notifications, new Transformer<Notification, String>() {
+
+                @Override
+                public String transform(final Notification notification) {
+                    return String.valueOf(notification.getKey());
+                }
+            }, new ArrayList<String>()));
+            throw sce;
+        }
+
+        MailTemplateTO deleted = getMailTemplateTO(key);
+        mailTemplateDAO.delete(key);
+        return deleted;
+    }
+
+    @Override
+    protected MailTemplateTO resolveReference(final Method method, final Object... args)
+            throws UnresolvedReferenceException {
+
+        String key = null;
+
+        if (ArrayUtils.isNotEmpty(args)) {
+            for (int i = 0; key == null && i < args.length; i++) {
+                if (args[i] instanceof Long) {
+                    key = (String) args[i];
+                } else if (args[i] instanceof MailTemplateTO) {
+                    key = ((MailTemplateTO) args[i]).getKey();
+                }
+            }
+        }
+
+        if ((key != null) && !key.equals(0L)) {
+            try {
+                return getMailTemplateTO(key);
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
index 8b523d9..7017843 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
@@ -19,15 +19,11 @@
 package org.apache.syncope.core.logic;
 
 import org.apache.syncope.core.misc.EntitlementsHolder;
-import java.io.IOException;
 import java.lang.reflect.Method;
 import java.net.URI;
-import java.util.HashSet;
-import java.util.Set;
 import javax.annotation.Resource;
 import org.apache.syncope.common.lib.to.SyncopeTO;
 import org.apache.syncope.core.misc.security.PasswordGenerator;
-import org.apache.syncope.core.misc.spring.ResourceWithFallbackLoader;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;
 import org.apache.syncope.core.persistence.api.ImplementationLookup.Type;
 import org.apache.syncope.core.persistence.api.dao.ConfDAO;
@@ -36,7 +32,6 @@ import org.apache.syncope.core.provisioning.api.ConnIdBundleManager;
 import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
 import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
 import org.apache.syncope.core.provisioning.api.cache.VirAttrCache;
-import org.apache.syncope.core.provisioning.java.notification.NotificationManagerImpl;
 import org.apache.syncope.core.workflow.api.AnyObjectWorkflowAdapter;
 import org.apache.syncope.core.workflow.api.GroupWorkflowAdapter;
 import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
@@ -85,9 +80,6 @@ public class SyncopeLogic extends AbstractLogic<SyncopeTO> {
     @Autowired
     private ImplementationLookup implLookup;
 
-    @Resource(name = "mailTemplateResourceLoader")
-    private ResourceWithFallbackLoader mailTemplateResourceLoader;
-
     @Transactional(readOnly = true)
     public boolean isSelfRegAllowed() {
         return confDAO.find("selfRegistration.allowed", "false").getValues().get(0).getBooleanValue();
@@ -147,30 +139,6 @@ public class SyncopeLogic extends AbstractLogic<SyncopeTO> {
         syncopeTO.getNotificationRecipientsProviders().
                 addAll(implLookup.getClassNames(Type.NOTIFICATION_RECIPIENTS_PROVIDER));
 
-        Set<String> htmlTemplates = new HashSet<>();
-        Set<String> textTemplates = new HashSet<>();
-        try {
-            for (org.springframework.core.io.Resource resource : mailTemplateResourceLoader.getResources(
-                    NotificationManagerImpl.MAIL_TEMPLATES + "*" + NotificationManagerImpl.MAIL_TEMPLATE_SUFFIX)) {
-
-                String template = resource.getURL().toExternalForm();
-                if (template.endsWith(NotificationManagerImpl.MAIL_TEMPLATE_HTML_SUFFIX)) {
-                    htmlTemplates.add(template.substring(template.indexOf(NotificationManagerImpl.MAIL_TEMPLATES) + 14,
-                            template.indexOf(NotificationManagerImpl.MAIL_TEMPLATE_HTML_SUFFIX)));
-                } else if (template.endsWith(NotificationManagerImpl.MAIL_TEMPLATE_TEXT_SUFFIX)) {
-                    textTemplates.add(template.substring(template.indexOf(NotificationManagerImpl.MAIL_TEMPLATES) + 14,
-                            template.indexOf(NotificationManagerImpl.MAIL_TEMPLATE_TEXT_SUFFIX)));
-                } else {
-                    LOG.warn("Unexpected template found: {}, ignoring...", template);
-                }
-            }
-        } catch (IOException e) {
-            LOG.error("While searching for mail templates", e);
-        }
-        // Only templates available both as HTML and TEXT are considered
-        htmlTemplates.retainAll(textTemplates);
-        syncopeTO.getMailTemplates().addAll(htmlTemplates);
-
         return syncopeTO;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/MailTemplateDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/MailTemplateDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/MailTemplateDAO.java
new file mode 100644
index 0000000..985462a
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/MailTemplateDAO.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.dao;
+
+import java.util.List;
+import org.apache.syncope.core.persistence.api.entity.MailTemplate;
+
+public interface MailTemplateDAO extends DAO<MailTemplate, String> {
+
+    MailTemplate find(String key);
+
+    List<MailTemplate> findAll();
+
+    MailTemplate save(MailTemplate notification);
+
+    void delete(String key);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/NotificationDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/NotificationDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/NotificationDAO.java
index 866e588..779cc0b 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/NotificationDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/NotificationDAO.java
@@ -19,12 +19,15 @@
 package org.apache.syncope.core.persistence.api.dao;
 
 import java.util.List;
+import org.apache.syncope.core.persistence.api.entity.MailTemplate;
 import org.apache.syncope.core.persistence.api.entity.Notification;
 
 public interface NotificationDAO extends DAO<Notification, Long> {
 
     Notification find(Long key);
 
+    List<Notification> findByTemplate(MailTemplate template);
+
     List<Notification> findAll();
 
     Notification save(Notification notification);

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/MailTemplate.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/MailTemplate.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/MailTemplate.java
new file mode 100644
index 0000000..e6ea97d
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/MailTemplate.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.entity;
+
+public interface MailTemplate extends Entity<String> {
+
+    void setKey(String key);
+
+    String getTextTemplate();
+
+    void setTextTemplate(String textTemplate);
+
+    String getHTMLTemplate();
+
+    void setHTMLTemplate(String htmlTemplate);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Notification.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Notification.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Notification.java
index dcda3d6..00bd23a 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Notification.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Notification.java
@@ -62,9 +62,9 @@ public interface Notification extends Entity<Long> {
 
     void setSubject(String subject);
 
-    String getTemplate();
+    MailTemplate getTemplate();
 
-    void setTemplate(String template);
+    void setTemplate(MailTemplate template);
 
     TraceLevel getTraceLevel();
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAMailTemplateDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAMailTemplateDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAMailTemplateDAO.java
new file mode 100644
index 0000000..8b31fcf
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAMailTemplateDAO.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.dao;
+
+import java.util.List;
+import javax.persistence.TypedQuery;
+import org.apache.syncope.core.persistence.api.dao.MailTemplateDAO;
+import org.apache.syncope.core.persistence.api.entity.MailTemplate;
+import org.apache.syncope.core.persistence.jpa.entity.JPAMailTemplate;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class JPAMailTemplateDAO extends AbstractDAO<MailTemplate, String> implements MailTemplateDAO {
+
+    @Override
+    public MailTemplate find(final String key) {
+        return entityManager().find(JPAMailTemplate.class, key);
+    }
+
+    @Override
+    public List<MailTemplate> findAll() {
+        TypedQuery<MailTemplate> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAMailTemplate.class.getSimpleName() + " e", MailTemplate.class);
+        return query.getResultList();
+    }
+
+    @Override
+    public MailTemplate save(final MailTemplate notification) {
+        return entityManager().merge(notification);
+    }
+
+    @Override
+    public void delete(final String key) {
+        MailTemplate template = find(key);
+        if (template == null) {
+            return;
+        }
+
+        entityManager().remove(template);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPANotificationDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPANotificationDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPANotificationDAO.java
index a43cf22..1a3da1a 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPANotificationDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPANotificationDAO.java
@@ -21,6 +21,7 @@ package org.apache.syncope.core.persistence.jpa.dao;
 import java.util.List;
 import javax.persistence.TypedQuery;
 import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
+import org.apache.syncope.core.persistence.api.entity.MailTemplate;
 import org.apache.syncope.core.persistence.api.entity.Notification;
 import org.apache.syncope.core.persistence.jpa.entity.JPANotification;
 import org.springframework.stereotype.Repository;
@@ -34,6 +35,15 @@ public class JPANotificationDAO extends AbstractDAO<Notification, Long> implemen
     }
 
     @Override
+    public List<Notification> findByTemplate(final MailTemplate template) {
+        TypedQuery<Notification> query = entityManager().createQuery(
+                "SELECT e FROM " + JPANotification.class.getSimpleName() + " e "
+                + "WHERE e.template=:template", Notification.class);
+        query.setParameter("template", template);
+        return query.getResultList();
+    }
+
+    @Override
     public List<Notification> findAll() {
         TypedQuery<Notification> query = entityManager().createQuery(
                 "SELECT e FROM " + JPANotification.class.getSimpleName() + " e", Notification.class);

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyAbout.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyAbout.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyAbout.java
index 4d7e9c5..e142820 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyAbout.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyAbout.java
@@ -47,7 +47,7 @@ public class JPAAnyAbout extends AbstractEntity<Long> implements AnyAbout {
     private JPAAnyType anyType;
 
     @Lob
-    private String about;
+    private String filter;
 
     @Override
     public Long getKey() {
@@ -78,12 +78,12 @@ public class JPAAnyAbout extends AbstractEntity<Long> implements AnyAbout {
 
     @Override
     public String get() {
-        return about;
+        return filter;
     }
 
     @Override
     public void set(final String filter) {
-        this.about = filter;
+        this.filter = filter;
     }
 
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
index 8fc70e3..1346cde 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
@@ -38,6 +38,7 @@ import org.apache.syncope.core.persistence.api.entity.Entity;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.Logger;
+import org.apache.syncope.core.persistence.api.entity.MailTemplate;
 import org.apache.syncope.core.persistence.api.entity.Notification;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
@@ -170,6 +171,8 @@ public class JPAEntityFactory implements EntityFactory {
             result = (T) new JPAConf();
         } else if (reference.equals(AnyAbout.class)) {
             result = (T) new JPAAnyAbout();
+        } else if (reference.equals(MailTemplate.class)) {
+            result = (T) new JPAMailTemplate();
         } else if (reference.equals(Notification.class)) {
             result = (T) new JPANotification();
         } else if (reference.equals(ExternalResource.class)) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAMailTemplate.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAMailTemplate.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAMailTemplate.java
new file mode 100644
index 0000000..2678df2
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAMailTemplate.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.entity;
+
+import javax.persistence.Cacheable;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Lob;
+import javax.persistence.Table;
+import org.apache.syncope.core.persistence.api.entity.MailTemplate;
+
+@Entity
+@Table(name = JPAMailTemplate.TABLE)
+@Cacheable
+public class JPAMailTemplate extends AbstractEntity<String> implements MailTemplate {
+
+    private static final long serialVersionUID = 2668267884059219835L;
+
+    public static final String TABLE = "MailTemplate";
+
+    @Id
+    private String name;
+
+    @Lob
+    private String textTemplate;
+
+    @Lob
+    private String htmlTemplate;
+
+    @Override
+    public String getKey() {
+        return name;
+    }
+
+    @Override
+    public void setKey(final String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String getTextTemplate() {
+        return textTemplate;
+    }
+
+    @Override
+    public void setTextTemplate(final String textTemplate) {
+        this.textTemplate = textTemplate;
+    }
+
+    @Override
+    public String getHTMLTemplate() {
+        return htmlTemplate;
+    }
+
+    @Override
+    public void setHTMLTemplate(final String htmlTemplate) {
+        this.htmlTemplate = htmlTemplate;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANotification.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANotification.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANotification.java
index 8afba79..2202a12 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANotification.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPANotification.java
@@ -31,6 +31,7 @@ import javax.persistence.Enumerated;
 import javax.persistence.FetchType;
 import javax.persistence.Id;
 import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
 import javax.validation.constraints.Max;
@@ -42,6 +43,7 @@ import org.apache.syncope.common.lib.types.IntMappingType;
 import org.apache.syncope.common.lib.types.TraceLevel;
 import org.apache.syncope.core.persistence.api.entity.AnyAbout;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
+import org.apache.syncope.core.persistence.api.entity.MailTemplate;
 import org.apache.syncope.core.persistence.api.entity.Notification;
 import org.apache.syncope.core.persistence.jpa.validation.entity.NotificationCheck;
 
@@ -97,8 +99,9 @@ public class JPANotification extends AbstractEntity<Long> implements Notificatio
     @NotNull
     private String subject;
 
-    @NotNull
-    private String template;
+    @ManyToOne(fetch = FetchType.EAGER, optional = false)
+    @JoinColumn(name = "template_name")
+    private JPAMailTemplate template;
 
     @NotNull
     @Enumerated(EnumType.STRING)
@@ -228,13 +231,14 @@ public class JPANotification extends AbstractEntity<Long> implements Notificatio
     }
 
     @Override
-    public String getTemplate() {
+    public MailTemplate getTemplate() {
         return template;
     }
 
     @Override
-    public void setTemplate(final String template) {
-        this.template = template;
+    public void setTemplate(final MailTemplate template) {
+        checkType(template, JPAMailTemplate.class);
+        this.template = (JPAMailTemplate) template;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
index ed1c035..361e7c9 100644
--- a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
@@ -116,14 +116,52 @@ under the License.
                validatorClass="org.apache.syncope.core.persistence.jpa.attrvalue.validation.EmailAddressValidator"/>
   
   <!-- Password reset notifications -->
+  <MailTemplate name="requestPasswordReset"
+                textTemplate="Hi,
+a password reset was request for ${user.getUsername()}.
+
+In order to complete this request, you need to visit this link:
+
+http://localhost:9080/syncope-console/?pwdResetToken=${input.get(0).replaceAll(' ', '%20')}
+
+If you did not request this reset, just ignore the present e-mail.
+
+Best regards."
+                htmlTemplate="&lt;html&gt;
+&lt;body&gt;
+&lt;p&gt;Hi,
+a password reset was request for ${user.getUsername()}.&lt;/p&gt;
+
+&lt;p&gt;In order to complete this request, you need to visit this 
+&lt;a href=&quot;http://localhost:9080/syncope-console/?pwdResetToken=${input.get(0).replaceAll(' ', '%20')}&quot;&gt;link&lt;/a&gt;&lt;/p&gt;.
+
+&lt;p&gt;If you did not request this reset, just ignore the present e-mail.&lt;/p&gt;
+
+&lt;p&gt;Best regards.&lt;/p&gt;
+&lt;/body&gt;
+&lt;/html&gt;"/>
+  <MailTemplate name="confirmPasswordReset"
+                textTemplate="Hi,
+we are happy to inform you that the password request was execute successfully for your account.
+
+Best regards."
+                htmlTemplate="&lt;html&gt;
+&lt;body&gt;
+&lt;p&gt;Hi,&lt;/br&gt;
+we are happy to inform you that the password request was execute successfully for your account.&lt;/p&gt;
+
+&lt;p&gt;Best regards.&lt;/p&gt;
+&lt;/body&gt;
+&lt;/html&gt;"/>
+
   <Notification id="1" active="1" recipientAttrName="email" recipientAttrType="UserPlainSchema" selfAsRecipient="1" 
-                sender="admin@syncope.apache.org" subject="Password Reset request" template="requestPasswordReset" 
+                sender="admin@syncope.apache.org" subject="Password Reset request" template_name="requestPasswordReset" 
                 traceLevel="FAILURES"/> 
-  <AnyAbout id="1" anyType_name="USER" notification_id="1" about="token!=$null"/>
+  <AnyAbout id="1" anyType_name="USER" notification_id="1" filter="token!=$null"/>
   <Notification_events Notification_id="1" event="[CUSTOM]:[]:[]:[requestPasswordReset]:[SUCCESS]"/>
   
   <Notification id="2" active="1" recipientAttrName="email" recipientAttrType="UserPlainSchema" selfAsRecipient="1" 
-                sender="admin@syncope.apache.org" subject="Password Reset successful" template="confirmPasswordReset" 
+                sender="admin@syncope.apache.org" subject="Password Reset successful" template_name="confirmPasswordReset" 
                 traceLevel="FAILURES"/> 
   <Notification_events Notification_id="2" event="[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"/>
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MailTemplateTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MailTemplateTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MailTemplateTest.java
new file mode 100644
index 0000000..772f4c1
--- /dev/null
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MailTemplateTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.inner;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.List;
+import org.apache.syncope.core.persistence.api.dao.MailTemplateDAO;
+import org.apache.syncope.core.persistence.api.entity.MailTemplate;
+import org.apache.syncope.core.persistence.jpa.AbstractTest;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional("Master")
+public class MailTemplateTest extends AbstractTest {
+
+    @Autowired
+    private MailTemplateDAO mailTemplateDAO;
+
+    @Test
+    public void find() {
+        MailTemplate optin = mailTemplateDAO.find("optin");
+        assertNotNull(optin);
+        assertNotNull(optin.getTextTemplate());
+        assertNotNull(optin.getHTMLTemplate());
+    }
+
+    @Test
+    public void findAll() {
+        List<MailTemplate> templates = mailTemplateDAO.findAll();
+        assertNotNull(templates);
+        assertFalse(templates.isEmpty());
+    }
+
+    @Test
+    public void save() {
+        MailTemplate template = entityFactory.newEntity(MailTemplate.class);
+        template.setKey("new");
+        template.setTextTemplate("Text template");
+
+        MailTemplate actual = mailTemplateDAO.save(template);
+        assertNotNull(actual);
+        assertNotNull(actual.getKey());
+        assertNotNull(actual.getTextTemplate());
+        assertNull(actual.getHTMLTemplate());
+
+        actual.setHTMLTemplate("<html><body><p>HTML template</p></body></html>");
+        actual = mailTemplateDAO.save(actual);
+        assertNotNull(actual.getTextTemplate());
+        assertNotNull(actual.getHTMLTemplate());
+    }
+
+    @Test
+    public void delete() {
+        mailTemplateDAO.delete("optin");
+        assertNull(mailTemplateDAO.find("optin"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/NotificationTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/NotificationTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/NotificationTest.java
index de1984c..4d88246 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/NotificationTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/NotificationTest.java
@@ -25,6 +25,7 @@ import static org.junit.Assert.assertNull;
 import java.util.List;
 import org.apache.syncope.common.lib.types.IntMappingType;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
+import org.apache.syncope.core.persistence.api.dao.MailTemplateDAO;
 import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
 import org.apache.syncope.core.persistence.api.entity.AnyAbout;
 import org.apache.syncope.core.persistence.api.entity.Notification;
@@ -42,6 +43,9 @@ public class NotificationTest extends AbstractTest {
     @Autowired
     private NotificationDAO notificationDAO;
 
+    @Autowired
+    private MailTemplateDAO mailTemplateDAO;
+
     @Test
     public void find() {
         Notification notification = notificationDAO.find(10L);
@@ -78,7 +82,7 @@ public class NotificationTest extends AbstractTest {
 
         notification.setSender("syncope@syncope.apache.org");
         notification.setSubject("Test notification");
-        notification.setTemplate("test");
+        notification.setTemplate(mailTemplateDAO.find("test"));
 
         Notification actual = notificationDAO.save(notification);
         assertNotNull(actual);
@@ -111,7 +115,7 @@ public class NotificationTest extends AbstractTest {
 
         notification.setSender("syncope@syncope.apache.org");
         notification.setSubject("Test notification");
-        notification.setTemplate("test");
+        notification.setTemplate(mailTemplateDAO.find("test"));
 
         Notification actual = notificationDAO.save(notification);
         assertNotNull(actual);
@@ -138,7 +142,7 @@ public class NotificationTest extends AbstractTest {
 
         notification.setSender("syncope@syncope.apache.org");
         notification.setSubject("Test notification");
-        notification.setTemplate("test");
+        notification.setTemplate(mailTemplateDAO.find("test"));
 
         Notification actual = notificationDAO.save(notification);
         assertNotNull(actual);

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index 10c8819..d78f0b0 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -1094,22 +1094,120 @@ under the License.
         destinationRealm_id="1" performCreate="1" performUpdate="1" performDelete="1" syncStatus="0" syncMode="INCREMENTAL"
         unmatchingRule="PROVISION" matchingRule="UPDATE" active="1"/>
 
+  <MailTemplate name="requestPasswordReset"
+                textTemplate="Hi,
+a password reset was request for ${user.getUsername()}.
+
+In order to complete this request, you need to visit this link:
+
+http://localhost:9080/syncope-console/?pwdResetToken=${input.get(0).replaceAll(' ', '%20')}
+
+If you did not request this reset, just ignore the present e-mail.
+
+Best regards."
+                htmlTemplate="&lt;html&gt;
+&lt;body&gt;
+&lt;p&gt;Hi,
+a password reset was request for ${user.getUsername()}.&lt;/p&gt;
+
+&lt;p&gt;In order to complete this request, you need to visit this 
+&lt;a href=&quot;http://localhost:9080/syncope-console/?pwdResetToken=${input.get(0).replaceAll(' ', '%20')}&quot;&gt;link&lt;/a&gt;&lt;/p&gt;.
+
+&lt;p&gt;If you did not request this reset, just ignore the present e-mail.&lt;/p&gt;
+
+&lt;p&gt;Best regards.&lt;/p&gt;
+&lt;/body&gt;
+&lt;/html&gt;"/>
+  <MailTemplate name="confirmPasswordReset"
+                textTemplate="Hi,
+we are happy to inform you that the password request was execute successfully for your account.
+
+Best regards."
+                htmlTemplate="&lt;html&gt;
+&lt;body&gt;
+&lt;p&gt;Hi,&lt;/br&gt;
+we are happy to inform you that the password request was execute successfully for your account.&lt;/p&gt;
+
+&lt;p&gt;Best regards.&lt;/p&gt;
+&lt;/body&gt;
+&lt;/html&gt;"/>
+  <MailTemplate name="test"/>
+  <MailTemplate name="optin"
+                textTemplate="Hi ${user.plainAttrMap[&quot;firstname&quot;].values[0]} ${user.plainAttrMap[&quot;surname&quot;].values[0]}, welcome to Syncope!
+
+Your username is ${user.username}.
+Your email address is ${user.plainAttrMap[&quot;email&quot;].values[0]}.
+Your email address inside a link: http://localhost/?email=${user.plainAttrMap[&quot;email&quot;].values[0].replace('@', '%40')}
+
+This message was sent to the following recipients:&#10;
+$$ for (recipient: recipients) {&#10;
+  * ${recipient.plainAttrMap[&quot;email&quot;].values[0]}&#10;
+$$ }&#10;
+&#10;
+because one of the following events occurred:&#10;
+$$ for (event: events) {&#10;
+  * ${event}&#10;
+$$ }&#10;
+&#10;
+$$ if (!empty(user.memberships)) {&#10;
+You have been provided with the following groups:&#10;
+$$ for(membership : user.memberships) {&#10;
+  * ${membership.groupName}&#10;
+$$ }&#10;
+$$ }&#10;"
+                htmlTemplate="&lt;html&gt;
+&lt;body&gt;
+&lt;h3&gt;Hi ${user.plainAttrMap[&quot;firstname&quot;].values[0]} ${user.plainAttrMap[&quot;surname&quot;].values[0]}, welcome to Syncope!&lt;/h3&gt;
+
+&lt;p&gt;
+   Your username is ${user.username}.&lt;br/&gt;
+   Your email address is ${user.plainAttrMap[&quot;email&quot;].values[0]}.
+   Your email address inside a &lt;a href=&quot;http://localhost/?email=${user.plainAttrMap[&quot;email&quot;].values[0].replace('@', '%40')}&quot;&gt;link&lt;/a&gt;.
+&lt;/p&gt;
+
+&lt;p&gt;
+    This message was sent to the following recipients:
+&lt;ul&gt;&#10;
+$$ for (recipient: recipients) {&#10;
+  &lt;li&gt;${recipient.plainAttrMap[&quot;email&quot;].values[0]}&lt;/li&gt;&#10;
+$$ }&#10;
+&lt;/ul&gt;&#10;
+
+because one of the following events occurred:
+&lt;ul&gt;&#10;
+$$ for (event: events) {&#10;
+  &lt;li&gt;${event}&lt;/i&gt;&#10;
+$$ }&#10;
+&lt;/ul&gt;&#10;
+&lt;/p&gt;
+&#10;
+$$ if (!empty(user.memberships)) {&#10;
+You have been provided with the following groups:&#10;
+&lt;ul&gt;&#10;
+$$ for(membership : user.memberships) {&#10;
+  &lt;li&gt;${membership.groupName}&lt;/li&gt;&#10;
+$$ }&#10;
+&lt;/ul&gt;&#10;
+$$ }&#10;
+&lt;/body&gt;
+&lt;/html&gt;"/>
+
   <Notification id="1" active="1" recipientAttrName="email" recipientAttrType="UserPlainSchema" selfAsRecipient="1" 
-                sender="admin@syncope.apache.org" subject="Password Reset request" template="requestPasswordReset" 
+                sender="admin@syncope.apache.org" subject="Password Reset request" template_name="requestPasswordReset" 
                 traceLevel="FAILURES"/> 
-  <AnyAbout id="1" anyType_name="USER" notification_id="1" about="token!=$null"/>
+  <AnyAbout id="1" anyType_name="USER" notification_id="1" filter="token!=$null"/>
   <Notification_events Notification_id="1" event="[CUSTOM]:[]:[]:[requestPasswordReset]:[SUCCESS]"/>
   
   <Notification id="2" active="1" recipientAttrName="email" recipientAttrType="UserPlainSchema" selfAsRecipient="1" 
-                sender="admin@syncope.apache.org" subject="Password Reset successful" template="confirmPasswordReset" 
+                sender="admin@syncope.apache.org" subject="Password Reset successful" template_name="confirmPasswordReset" 
                 traceLevel="FAILURES"/> 
   <Notification_events Notification_id="2" event="[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"/>
 
-  <Notification id="10" sender="test@syncope.apache.org" subject="Test subject" template="test" selfAsRecipient="0" 
+  <Notification id="10" sender="test@syncope.apache.org" subject="Test subject" template_name="test" selfAsRecipient="0" 
                 traceLevel="FAILURES"
                 recipientsFIQL="$groups==7"
                 recipientAttrType="UserPlainSchema" recipientAttrName="email" active="1"/>
-  <AnyAbout id="10" anyType_name="USER" notification_id="10" about="fullname==*o*;fullname==*i*"/>
+  <AnyAbout id="10" anyType_name="USER" notification_id="10" filter="fullname==*o*;fullname==*i*"/>
   <Notification_events Notification_id="10" event="[CUSTOM]:[]:[]:[unexisting1]:[FAILURE]"/>
   <Notification_events Notification_id="10" event="[CUSTOM]:[]:[]:[unexisting2]:[SUCCESS]"/>
     

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/persistence-jpa/src/test/resources/domains/TwoContent.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/resources/domains/TwoContent.xml b/core/persistence-jpa/src/test/resources/domains/TwoContent.xml
index cc36a9d..14c65a8 100644
--- a/core/persistence-jpa/src/test/resources/domains/TwoContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/TwoContent.xml
@@ -102,14 +102,52 @@ under the License.
                validatorClass="org.apache.syncope.core.persistence.jpa.attrvalue.validation.EmailAddressValidator"/>
   
   <!-- Password reset notifications -->
+  <MailTemplate name="requestPasswordReset"
+                  textTemplate="Hi,
+a password reset was request for ${user.getUsername()}.
+
+In order to complete this request, you need to visit this link:
+
+http://localhost:9080/syncope-console/?pwdResetToken=${input.get(0).replaceAll(' ', '%20')}
+
+If you did not request this reset, just ignore the present e-mail.
+
+Best regards."
+                  htmlTemplate="&lt;html&gt;
+&lt;body&gt;
+&lt;p&gt;Hi,
+a password reset was request for ${user.getUsername()}.&lt;/p&gt;
+
+&lt;p&gt;In order to complete this request, you need to visit this 
+&lt;a href=&quot;http://localhost:9080/syncope-console/?pwdResetToken=${input.get(0).replaceAll(' ', '%20')}&quot;&gt;link&lt;/a&gt;&lt;/p&gt;.
+
+&lt;p&gt;If you did not request this reset, just ignore the present e-mail.&lt;/p&gt;
+
+&lt;p&gt;Best regards.&lt;/p&gt;
+&lt;/body&gt;
+&lt;/html&gt;"/>
+  <MailTemplate name="confirmPasswordReset"
+                textTemplate="Hi,
+we are happy to inform you that the password request was execute successfully for your account.
+
+Best regards."
+                htmlTemplate="&lt;html&gt;
+&lt;body&gt;
+&lt;p&gt;Hi,&lt;/br&gt;
+we are happy to inform you that the password request was execute successfully for your account.&lt;/p&gt;
+
+&lt;p&gt;Best regards.&lt;/p&gt;
+&lt;/body&gt;
+&lt;/html&gt;"/>
+
   <Notification id="1" active="1" recipientAttrName="email" recipientAttrType="UserPlainSchema" selfAsRecipient="1" 
-                sender="admin@syncope.apache.org" subject="Password Reset request" template="requestPasswordReset" 
+                sender="admin@syncope.apache.org" subject="Password Reset request" template_name="requestPasswordReset" 
                 traceLevel="FAILURES"/> 
-  <AnyAbout id="1" anyType_name="USER" notification_id="1" about="token!=$null"/>
+  <AnyAbout id="1" anyType_name="USER" notification_id="1" filter="token!=$null"/>
   <Notification_events Notification_id="1" event="[CUSTOM]:[]:[]:[requestPasswordReset]:[SUCCESS]"/>
   
   <Notification id="2" active="1" recipientAttrName="email" recipientAttrType="UserPlainSchema" selfAsRecipient="1" 
-                sender="admin@syncope.apache.org" subject="Password Reset successful" template="confirmPasswordReset" 
+                sender="admin@syncope.apache.org" subject="Password Reset successful" template_name="confirmPasswordReset" 
                 traceLevel="FAILURES"/> 
   <Notification_events Notification_id="2" event="[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"/>
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java
index 2b425c5..020f280 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java
@@ -28,6 +28,7 @@ import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.Notification;
 import org.apache.syncope.core.misc.spring.BeanUtils;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
+import org.apache.syncope.core.persistence.api.dao.MailTemplateDAO;
 import org.apache.syncope.core.persistence.api.entity.AnyAbout;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.slf4j.Logger;
@@ -40,7 +41,10 @@ public class NotificationDataBinderImpl implements NotificationDataBinder {
 
     private static final Logger LOG = LoggerFactory.getLogger(NotificationDataBinder.class);
 
-    private static final String[] IGNORE_PROPERTIES = { "key", "abouts" };
+    private static final String[] IGNORE_PROPERTIES = { "key", "template", "abouts" };
+
+    @Autowired
+    private MailTemplateDAO mailTemplateDAO;
 
     @Autowired
     private AnyTypeDAO anyTypeDAO;
@@ -55,6 +59,8 @@ public class NotificationDataBinderImpl implements NotificationDataBinder {
         BeanUtils.copyProperties(notification, result, IGNORE_PROPERTIES);
 
         result.setKey(notification.getKey());
+        result.setTemplate(notification.getTemplate().getKey());
+
         for (AnyAbout about : notification.getAbouts()) {
             result.getAbouts().put(about.getAnyType().getKey(), about.get());
         }
@@ -73,6 +79,8 @@ public class NotificationDataBinderImpl implements NotificationDataBinder {
     public void update(final Notification notification, final NotificationTO notificationTO) {
         BeanUtils.copyProperties(notificationTO, notification, IGNORE_PROPERTIES);
 
+        notification.setTemplate(mailTemplateDAO.find(notificationTO.getTemplate()));
+
         // 1. add or update all (valid) abouts from TO
         for (Map.Entry<String, String> entry : notificationTO.getAbouts().entrySet()) {
             if (StringUtils.isNotBlank(entry.getValue())) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
index 207323d..74dc39f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.core.provisioning.java.notification;
 
-import java.io.IOException;
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -27,9 +26,8 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import javax.annotation.Resource;
-import org.apache.commons.io.IOUtils;
 import org.apache.commons.jexl3.MapContext;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AuditElements;
@@ -58,7 +56,6 @@ import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
 import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 import org.apache.syncope.core.misc.search.SearchCondConverter;
 import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
-import org.apache.syncope.core.misc.spring.ResourceWithFallbackLoader;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
@@ -85,14 +82,6 @@ public class NotificationManagerImpl implements NotificationManager {
 
     private static final Logger LOG = LoggerFactory.getLogger(NotificationManager.class);
 
-    public static final String MAIL_TEMPLATES = "mailTemplates/";
-
-    public static final String MAIL_TEMPLATE_SUFFIX = ".jexl3";
-
-    public static final String MAIL_TEMPLATE_HTML_SUFFIX = ".html.jexl3";
-
-    public static final String MAIL_TEMPLATE_TEXT_SUFFIX = ".txt.jexl3";
-
     @Autowired
     private DerSchemaDAO derSchemaDAO;
 
@@ -141,9 +130,6 @@ public class NotificationManagerImpl implements NotificationManager {
     @Autowired
     private TaskDAO taskDAO;
 
-    @Resource(name = "mailTemplateResourceLoader")
-    private ResourceWithFallbackLoader mailTemplateResourceLoader;
-
     @Autowired
     private DerAttrHandler derAttrHander;
 
@@ -235,30 +221,21 @@ public class NotificationManagerImpl implements NotificationManager {
         task.setSender(notification.getSender());
         task.setSubject(notification.getSubject());
 
-        String htmlBody = evaluate(
-                MAIL_TEMPLATES + notification.getTemplate() + MAIL_TEMPLATE_HTML_SUFFIX, jexlVars);
-        String textBody = evaluate(
-                MAIL_TEMPLATES + notification.getTemplate() + MAIL_TEMPLATE_TEXT_SUFFIX, jexlVars);
-
-        task.setHtmlBody(htmlBody);
-        task.setTextBody(textBody);
+        if (StringUtils.isNotBlank(notification.getTemplate().getTextTemplate())) {
+            task.setTextBody(evaluate(notification.getTemplate().getTextTemplate(), jexlVars));
+        }
+        if (StringUtils.isNotBlank(notification.getTemplate().getHTMLTemplate())) {
+            task.setHtmlBody(evaluate(notification.getTemplate().getHTMLTemplate(), jexlVars));
+        }
 
         return task;
     }
 
-    private String evaluate(final String templateLocation, final Map<String, Object> jexlVars) {
-        org.springframework.core.io.Resource templateResource =
-                mailTemplateResourceLoader.getResource(templateLocation);
-
+    private String evaluate(final String template, final Map<String, Object> jexlVars) {
         StringWriter writer = new StringWriter();
-        try {
-            JexlUtils.newJxltEngine().
-                    createTemplate(IOUtils.toString(templateResource.getInputStream())).
-                    evaluate(new MapContext(jexlVars), writer);
-        } catch (final IOException e) {
-            LOG.error("Could not get mail body from {}", templateResource, e);
-        }
-
+        JexlUtils.newJxltEngine().
+                createTemplate(template).
+                evaluate(new MapContext(jexlVars), writer);
         return writer.toString();
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/provisioning-java/src/main/resources/mail.properties
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/mail.properties b/core/provisioning-java/src/main/resources/mail.properties
index 12f04e7..7a6653e 100644
--- a/core/provisioning-java/src/main/resources/mail.properties
+++ b/core/provisioning-java/src/main/resources/mail.properties
@@ -14,7 +14,6 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-templates.directory=${conf.directory}
 smtpHost=none.syncope.apache.org
 smtpPort=25
 smtpUser=

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.jexl3
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.jexl3 b/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.jexl3
deleted file mode 100644
index f07ed07..0000000
--- a/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.jexl3
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations
-under the License.
-*/
-<html>
-<body>
-<p>Hi,</br>
-we are happy to inform you that the password request was execute successfully for your account.</p>
-
-<p>Best regards.</p>
-</body>
-</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.jexl3
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.jexl3 b/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.jexl3
deleted file mode 100644
index a42bdd7..0000000
--- a/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.jexl3
+++ /dev/null
@@ -1,20 +0,0 @@
-## Licensed to the Apache Software Foundation (ASF) under one
-## or more contributor license agreements.  See the NOTICE file
-## distributed with this work for additional information
-## regarding copyright ownership.  The ASF licenses this file
-## to you under the Apache License, Version 2.0 (the
-## "License"); you may not use this file except in compliance
-## with the License.  You may obtain a copy of the License at
-##
-##   http://www.apache.org/licenses/LICENSE-2.0
-##
-## Unless required by applicable law or agreed to in writing,
-## software distributed under the License is distributed on an
-## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-## KIND, either express or implied.  See the License for the
-## specific language governing permissions and limitations
-## under the License.
-Hi,
-we are happy to inform you that the password request was execute successfully for your account.
-
-Best regards.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/provisioning-java/src/main/resources/mailTemplates/optin.html.jexl3
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/mailTemplates/optin.html.jexl3 b/core/provisioning-java/src/main/resources/mailTemplates/optin.html.jexl3
deleted file mode 100644
index 3d2fa2f..0000000
--- a/core/provisioning-java/src/main/resources/mailTemplates/optin.html.jexl3
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations
-under the License.
-*/
-<html>
-<body>
-<h3>Hi ${user.plainAttrMap["firstname"].values[0]} ${user.plainAttrMap["surname"].values[0]}, welcome to Syncope!</h3>
-
-<p>
-   Your username is ${user.username}.<br/>
-   Your email address is ${user.plainAttrMap["email"].values[0]}.
-   Your email address inside a <a href="http://localhost/?email=${user.plainAttrMap["email"].values[0].replace('@', '%40')}">link</a>.
-</p>
-
-<p>
-    This message was sent to the following recipients:
-<ul>
-$$ for (recipient: recipients) {
-  <li>${recipient.plainAttrMap["email"].values[0]}</li>
-$$ }
-</ul>
-
-because one of the following events occurred:
-<ul>
-$$ for (event: events) {
-  <li>${event}</i>
-$$ }
-</ul>
-</p>
-
-$$ if (!empty(user.memberships)) {
-You have been provided with the following groups:
-<ul>
-$$ for(membership : user.memberships) {
-  <li>${membership.groupName}</li>
-$$ }
-</ul>
-$$ }
-</body>
-</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/2ac6f8d5/core/provisioning-java/src/main/resources/mailTemplates/optin.txt.jexl3
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/mailTemplates/optin.txt.jexl3 b/core/provisioning-java/src/main/resources/mailTemplates/optin.txt.jexl3
deleted file mode 100644
index c8fee4e..0000000
--- a/core/provisioning-java/src/main/resources/mailTemplates/optin.txt.jexl3
+++ /dev/null
@@ -1,38 +0,0 @@
-## Licensed to the Apache Software Foundation (ASF) under one
-## or more contributor license agreements.  See the NOTICE file
-## distributed with this work for additional information
-## regarding copyright ownership.  The ASF licenses this file
-## to you under the Apache License, Version 2.0 (the
-## "License"); you may not use this file except in compliance
-## with the License.  You may obtain a copy of the License at
-##
-##   http://www.apache.org/licenses/LICENSE-2.0
-##
-## Unless required by applicable law or agreed to in writing,
-## software distributed under the License is distributed on an
-## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-## KIND, either express or implied.  See the License for the
-## specific language governing permissions and limitations
-## under the License.
-Hi ${user.plainAttrMap["firstname"].values[0]} ${user.plainAttrMap["surname"].values[0]}, welcome to Syncope!
-
-Your username is ${user.username}.<br/>
-Your email address is ${user.plainAttrMap["email"].values[0]}.
-Your email address inside a link: http://localhost/?email=${user.plainAttrMap["email"].values[0].replace('@', '%40')}
-
-This message was sent to the following recipients:
-$$ for (recipient: recipients) {
-  * ${recipient.plainAttrMap["email"].values[0]}
-$$ }
-
-because one of the following events occurred:
-$$ for (event: events) {
-  * ${event}
-$$ }
-
-$$ if (!empty(user.memberships)) {
-You have been provided with the following groups:
-$$ for(membership : user.memberships) {
-  * ${membership.groupName}
-$$ }
-$$ }