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/28 18:14:14 UTC

syncope git commit: [SYNCOPE-760] Preliminary change: ditching Velocity for JEXL

Repository: syncope
Updated Branches:
  refs/heads/master bc4976f5a -> fa28888fe


[SYNCOPE-760] Preliminary change: ditching Velocity for JEXL


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

Branch: refs/heads/master
Commit: fa28888fed98a440f4c510107a9494e54ef192f2
Parents: bc4976f
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Jan 28 18:14:07 2016 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Jan 28 18:14:07 2016 +0100

----------------------------------------------------------------------
 .../console/SyncopeConsoleApplication.java      |   2 +-
 .../apache/syncope/core/logic/SyncopeLogic.java |   8 +-
 .../syncope/core/misc/jexl/JexlUtils.java       |   5 +
 core/provisioning-java/pom.xml                  |   9 --
 .../notification/NotificationManagerImpl.java   |  85 +++++-------
 .../SpringVelocityResourceLoader.java           |  84 ------------
 .../notification/VelocityEngineFactoryBean.java | 104 --------------
 .../confirmPasswordReset.html.jexl3             |  26 ++++
 .../mailTemplates/confirmPasswordReset.html.vm  |  26 ----
 .../confirmPasswordReset.txt.jexl3              |  20 +++
 .../mailTemplates/confirmPasswordReset.txt.vm   |  20 ---
 .../resources/mailTemplates/optin.html.jexl3    |  54 ++++++++
 .../main/resources/mailTemplates/optin.html.vm  |  72 ----------
 .../resources/mailTemplates/optin.txt.jexl3     |  38 ++++++
 .../main/resources/mailTemplates/optin.txt.vm   |  51 -------
 .../requestPasswordReset.html.jexl3             |  31 +++++
 .../mailTemplates/requestPasswordReset.html.vm  |  31 -----
 .../requestPasswordReset.txt.jexl3              |  26 ++++
 .../mailTemplates/requestPasswordReset.txt.vm   |  26 ----
 .../src/main/resources/provisioningContext.xml  |  13 +-
 .../provisioning/java/MailTemplateTest.java     | 136 +++++++++++++++++++
 deb/core/pom.xml                                |   2 +-
 deb/core/src/deb/control/conffiles              |  12 +-
 .../fit/core/NotificationTaskITCase.java        |   2 +-
 pom.xml                                         |  29 ----
 25 files changed, 384 insertions(+), 528 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/fa28888f/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java b/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java
index 1e3c786..b695c84 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java
@@ -160,7 +160,7 @@ public class SyncopeConsoleApplication extends AuthenticatedWebApplication {
         clientFactory = new SyncopeClientFactoryBean().setAddress(scheme + "://" + host + ":" + port + "/" + rootPath);
 
         // process page properties
-        synchronized (CONSOLE_PROPERTIES) {
+        synchronized (LOG) {
             if (PAGE_CLASSES == null) {
                 PAGE_CLASSES = new HashMap<>();
                 populatePageClasses(props);

http://git-wip-us.apache.org/repos/asf/syncope/blob/fa28888f/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 e86db66..8b523d9 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
@@ -85,8 +85,8 @@ public class SyncopeLogic extends AbstractLogic<SyncopeTO> {
     @Autowired
     private ImplementationLookup implLookup;
 
-    @Resource(name = "velocityResourceLoader")
-    private ResourceWithFallbackLoader resourceLoader;
+    @Resource(name = "mailTemplateResourceLoader")
+    private ResourceWithFallbackLoader mailTemplateResourceLoader;
 
     @Transactional(readOnly = true)
     public boolean isSelfRegAllowed() {
@@ -150,8 +150,8 @@ public class SyncopeLogic extends AbstractLogic<SyncopeTO> {
         Set<String> htmlTemplates = new HashSet<>();
         Set<String> textTemplates = new HashSet<>();
         try {
-            for (org.springframework.core.io.Resource resource : resourceLoader.getResources(
-                    NotificationManagerImpl.MAIL_TEMPLATES + "*.vm")) {
+            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)) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/fa28888f/core/misc/src/main/java/org/apache/syncope/core/misc/jexl/JexlUtils.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/jexl/JexlUtils.java b/core/misc/src/main/java/org/apache/syncope/core/misc/jexl/JexlUtils.java
index b03c027..9c64734 100644
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/jexl/JexlUtils.java
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/jexl/JexlUtils.java
@@ -31,6 +31,7 @@ import org.apache.commons.jexl3.JexlContext;
 import org.apache.commons.jexl3.JexlEngine;
 import org.apache.commons.jexl3.JexlException;
 import org.apache.commons.jexl3.JexlExpression;
+import org.apache.commons.jexl3.JxltEngine;
 import org.apache.commons.jexl3.MapContext;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -72,6 +73,10 @@ public final class JexlUtils {
         return JEXL_ENGINE;
     }
 
+    public static JxltEngine newJxltEngine() {
+        return getEngine().createJxltEngine(false);
+    }
+
     public static boolean isExpressionValid(final String expression) {
         boolean result;
         try {

http://git-wip-us.apache.org/repos/asf/syncope/blob/fa28888f/core/provisioning-java/pom.xml
----------------------------------------------------------------------
diff --git a/core/provisioning-java/pom.xml b/core/provisioning-java/pom.xml
index 85ea59f..ee6bfc6 100644
--- a/core/provisioning-java/pom.xml
+++ b/core/provisioning-java/pom.xml
@@ -53,15 +53,6 @@ under the License.
       <groupId>org.apache.geronimo.javamail</groupId>
       <artifactId>geronimo-javamail_1.4_mail</artifactId>
     </dependency>
-
-    <dependency>
-      <groupId>org.apache.velocity</groupId>
-      <artifactId>velocity</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.velocity</groupId>
-      <artifactId>velocity-tools</artifactId>
-    </dependency>
     
     <dependency>
       <groupId>org.apache.syncope.core</groupId>

http://git-wip-us.apache.org/repos/asf/syncope/blob/fa28888f/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 09594d4..207323d 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,7 @@
  */
 package org.apache.syncope.core.provisioning.java.notification;
 
-import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
+import java.io.IOException;
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -27,7 +27,9 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import org.apache.syncope.common.lib.SyncopeConstants;
+import javax.annotation.Resource;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.jexl3.MapContext;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AuditElements;
@@ -37,6 +39,7 @@ import org.apache.syncope.common.lib.types.IntMappingType;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.misc.jexl.JexlUtils;
 import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
@@ -55,6 +58,7 @@ 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;
@@ -66,12 +70,8 @@ import org.apache.syncope.core.persistence.api.entity.DerSchema;
 import org.apache.syncope.core.persistence.api.entity.VirSchema;
 import org.apache.syncope.core.provisioning.api.DerAttrHandler;
 import org.apache.syncope.core.provisioning.api.VirAttrHandler;
+import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 import org.apache.syncope.core.provisioning.api.notification.NotificationRecipientsProvider;
-import org.apache.velocity.VelocityContext;
-import org.apache.velocity.app.VelocityEngine;
-import org.apache.velocity.context.Context;
-import org.apache.velocity.exception.VelocityException;
-import org.apache.velocity.tools.ToolManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -87,9 +87,11 @@ public class NotificationManagerImpl implements NotificationManager {
 
     public static final String MAIL_TEMPLATES = "mailTemplates/";
 
-    public static final String MAIL_TEMPLATE_HTML_SUFFIX = ".html.vm";
+    public static final String MAIL_TEMPLATE_SUFFIX = ".jexl3";
 
-    public static final String MAIL_TEMPLATE_TEXT_SUFFIX = ".txt.vm";
+    public static final String MAIL_TEMPLATE_HTML_SUFFIX = ".html.jexl3";
+
+    public static final String MAIL_TEMPLATE_TEXT_SUFFIX = ".txt.jexl3";
 
     @Autowired
     private DerSchemaDAO derSchemaDAO;
@@ -139,17 +141,8 @@ public class NotificationManagerImpl implements NotificationManager {
     @Autowired
     private TaskDAO taskDAO;
 
-    /**
-     * Velocity template engine.
-     */
-    @Autowired
-    private VelocityEngine velocityEngine;
-
-    /**
-     * Velocity tool manager.
-     */
-    @Autowired
-    private ToolManager velocityToolManager;
+    @Resource(name = "mailTemplateResourceLoader")
+    private ResourceWithFallbackLoader mailTemplateResourceLoader;
 
     @Autowired
     private DerAttrHandler derAttrHander;
@@ -177,13 +170,13 @@ public class NotificationManagerImpl implements NotificationManager {
      *
      * @param notification notification to take as model
      * @param any the any object this task is about
-     * @param model Velocity model
+     * @param jexlVars JEXL variables
      * @return notification task, fully populated
      */
     private NotificationTask getNotificationTask(
             final Notification notification,
             final Any<?> any,
-            final Map<String, Object> model) {
+            final Map<String, Object> jexlVars) {
 
         if (any != null) {
             virAttrHander.getValues(any);
@@ -232,9 +225,9 @@ public class NotificationManagerImpl implements NotificationManager {
             }
         }
 
-        model.put("recipients", recipientTOs);
-        model.put("syncopeConf", this.findAllSyncopeConfs());
-        model.put("events", notification.getEvents());
+        jexlVars.put("recipients", recipientTOs);
+        jexlVars.put("syncopeConf", this.findAllSyncopeConfs());
+        jexlVars.put("events", notification.getEvents());
 
         NotificationTask task = entityFactory.newEntity(NotificationTask.class);
         task.setTraceLevel(notification.getTraceLevel());
@@ -242,10 +235,10 @@ public class NotificationManagerImpl implements NotificationManager {
         task.setSender(notification.getSender());
         task.setSubject(notification.getSubject());
 
-        String htmlBody = mergeTemplateIntoString(
-                MAIL_TEMPLATES + notification.getTemplate() + MAIL_TEMPLATE_HTML_SUFFIX, model);
-        String textBody = mergeTemplateIntoString(
-                MAIL_TEMPLATES + notification.getTemplate() + MAIL_TEMPLATE_TEXT_SUFFIX, model);
+        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);
@@ -253,32 +246,20 @@ public class NotificationManagerImpl implements NotificationManager {
         return task;
     }
 
-    private String mergeTemplateIntoString(final String templateLocation, final Map<String, Object> model) {
-        StringWriter result = new StringWriter();
+    private String evaluate(final String templateLocation, final Map<String, Object> jexlVars) {
+        org.springframework.core.io.Resource templateResource =
+                mailTemplateResourceLoader.getResource(templateLocation);
+
+        StringWriter writer = new StringWriter();
         try {
-            Context velocityContext = createVelocityContext(model);
-            velocityEngine.mergeTemplate(templateLocation, SyncopeConstants.DEFAULT_ENCODING, velocityContext, result);
-        } catch (VelocityException e) {
-            LOG.error("Could not get mail body", e);
-        } catch (RuntimeException e) {
-            // ensure same behaviour as by using Spring VelocityEngineUtils.mergeTemplateIntoString()
-            throw e;
-        } catch (Exception e) {
-            LOG.error("Could not get mail body", e);
+            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);
         }
 
-        return result.toString();
-    }
-
-    /**
-     * Create a Velocity Context for the given model, to be passed to the template for merging.
-     *
-     * @param model Velocity model
-     * @return Velocity context
-     */
-    protected Context createVelocityContext(final Map<String, Object> model) {
-        Context toolContext = velocityToolManager.createContext();
-        return new VelocityContext(model, toolContext);
+        return writer.toString();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/fa28888f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/SpringVelocityResourceLoader.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/SpringVelocityResourceLoader.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/SpringVelocityResourceLoader.java
deleted file mode 100644
index feafc34..0000000
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/SpringVelocityResourceLoader.java
+++ /dev/null
@@ -1,84 +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.
- */
-package org.apache.syncope.core.provisioning.java.notification;
-
-import java.io.IOException;
-import java.io.InputStream;
-import org.apache.commons.collections.ExtendedProperties;
-import org.apache.velocity.exception.ResourceNotFoundException;
-import org.apache.velocity.runtime.resource.Resource;
-import org.apache.velocity.runtime.resource.loader.ResourceLoader;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Velocity ResourceLoader adapter that loads via a Spring ResourceLoader.
- * Similar to <tt>org.springframework.ui.velocity.SpringResourceLoader</tt> but more integrated with
- * {@link VelocityEngineFactoryBean}.
- */
-public class SpringVelocityResourceLoader extends ResourceLoader {
-
-    private static final Logger LOG = LoggerFactory.getLogger(SpringVelocityResourceLoader.class);
-
-    public static final String NAME = "spring";
-
-    public static final String SPRING_RESOURCE_LOADER_CLASS = "spring.resource.loader.class";
-
-    public static final String SPRING_RESOURCE_LOADER_CACHE = "spring.resource.loader.cache";
-
-    public static final String SPRING_RESOURCE_LOADER = "spring.resource.loader";
-
-    private org.springframework.core.io.ResourceLoader resourceLoader;
-
-    @Override
-    public void init(final ExtendedProperties configuration) {
-        this.resourceLoader =
-                (org.springframework.core.io.ResourceLoader) this.rsvc.getApplicationAttribute(SPRING_RESOURCE_LOADER);
-        if (this.resourceLoader == null) {
-            throw new IllegalArgumentException(
-                    "'" + SPRING_RESOURCE_LOADER + "' application attribute must be present for SpringResourceLoader");
-        }
-
-        LOG.info("SpringResourceLoader for Velocity: using resource loader [" + this.resourceLoader + "]");
-    }
-
-    @Override
-    public InputStream getResourceStream(final String source) {
-        LOG.debug("Looking for Velocity resource with name [{}]", source);
-
-        org.springframework.core.io.Resource resource = this.resourceLoader.getResource(source);
-        try {
-            return resource.getInputStream();
-        } catch (IOException e) {
-            LOG.debug("Could not find Velocity resource: " + resource, e);
-        }
-        throw new ResourceNotFoundException("Could not find resource [" + source + "]");
-    }
-
-    @Override
-    public boolean isSourceModified(final Resource resource) {
-        return false;
-    }
-
-    @Override
-    public long getLastModified(final Resource resource) {
-        return 0;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/fa28888f/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/VelocityEngineFactoryBean.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/VelocityEngineFactoryBean.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/VelocityEngineFactoryBean.java
deleted file mode 100644
index 6e2b9d3..0000000
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/VelocityEngineFactoryBean.java
+++ /dev/null
@@ -1,104 +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.
- */
-package org.apache.syncope.core.provisioning.java.notification;
-
-import java.io.IOException;
-
-import org.apache.velocity.app.VelocityEngine;
-import org.apache.velocity.runtime.RuntimeConstants;
-import org.apache.velocity.runtime.log.CommonsLogLogChute;
-import org.springframework.beans.factory.FactoryBean;
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.core.io.DefaultResourceLoader;
-import org.springframework.core.io.ResourceLoader;
-
-/**
- * Similar to Spring's equivalent (<tt>org.springframework.ui.velocity.VelocityEngineFactoryBean</tt>), does not
- * implement {@link org.springframework.context.ResourceLoaderAware} thus allowing custom injection.
- */
-public class VelocityEngineFactoryBean implements FactoryBean<VelocityEngine>, InitializingBean {
-
-    private ResourceLoader resourceLoader = new DefaultResourceLoader();
-
-    private boolean overrideLogging = true;
-
-    private VelocityEngine velocityEngine;
-
-    public ResourceLoader getResourceLoader() {
-        return resourceLoader;
-    }
-
-    public void setResourceLoader(final ResourceLoader resourceLoader) {
-        this.resourceLoader = resourceLoader;
-    }
-
-    public boolean isOverrideLogging() {
-        return overrideLogging;
-    }
-
-    /**
-     * Configure Velocity to use Commons Logging (true by default).
-     *
-     * @param overrideLogging whether default Velocity logging should be overriden or not.
-     */
-    public void setOverrideLogging(final boolean overrideLogging) {
-        this.overrideLogging = overrideLogging;
-    }
-
-    private void createVelocityEngine() throws IOException {
-        velocityEngine = new VelocityEngine();
-
-        velocityEngine.setProperty(
-                RuntimeConstants.RESOURCE_LOADER, SpringVelocityResourceLoader.NAME);
-        velocityEngine.setProperty(
-                SpringVelocityResourceLoader.SPRING_RESOURCE_LOADER_CLASS,
-                SpringVelocityResourceLoader.class.getName());
-        velocityEngine.setProperty(
-                SpringVelocityResourceLoader.SPRING_RESOURCE_LOADER_CACHE, "true");
-        velocityEngine.setApplicationAttribute(
-                SpringVelocityResourceLoader.SPRING_RESOURCE_LOADER, getResourceLoader());
-
-        if (this.overrideLogging) {
-            velocityEngine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new CommonsLogLogChute());
-        }
-
-        velocityEngine.init();
-    }
-
-    @Override
-    public void afterPropertiesSet() throws IOException {
-        createVelocityEngine();
-    }
-
-    @Override
-    public VelocityEngine getObject() {
-        return this.velocityEngine;
-    }
-
-    @Override
-    public Class<? extends VelocityEngine> getObjectType() {
-        return VelocityEngine.class;
-    }
-
-    @Override
-    public boolean isSingleton() {
-        return true;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/fa28888f/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
new file mode 100644
index 0000000..f07ed07
--- /dev/null
+++ b/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.jexl3
@@ -0,0 +1,26 @@
+/*
+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/fa28888f/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.vm
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.vm b/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.vm
deleted file mode 100644
index 90630ac..0000000
--- a/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.vm
+++ /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/fa28888f/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
new file mode 100644
index 0000000..a42bdd7
--- /dev/null
+++ b/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.jexl3
@@ -0,0 +1,20 @@
+## 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/fa28888f/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.vm
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.vm b/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.vm
deleted file mode 100644
index 33f75dc..0000000
--- a/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.vm
+++ /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/fa28888f/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
new file mode 100644
index 0000000..3d2fa2f
--- /dev/null
+++ b/core/provisioning-java/src/main/resources/mailTemplates/optin.html.jexl3
@@ -0,0 +1,54 @@
+/*
+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/fa28888f/core/provisioning-java/src/main/resources/mailTemplates/optin.html.vm
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/mailTemplates/optin.html.vm b/core/provisioning-java/src/main/resources/mailTemplates/optin.html.vm
deleted file mode 100644
index 3f36035..0000000
--- a/core/provisioning-java/src/main/resources/mailTemplates/optin.html.vm
+++ /dev/null
@@ -1,72 +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.getPlainAttrMap().get("firstname").getValues().get(0) $user.getPlainAttrMap().get("surname").getValues().get(0), welcome to Syncope!</h3>
-
-<p>
-   Your username is $user.getUsername().<br/>
-   Your email address is $user.getPlainAttrMap().get("email").getValues().get(0).
-   Your email address inside a <a href="http://localhost/?email=$esc.url($user.getPlainAttrMap().get("email").getValues().get(0))">link</a>.
-</p>
-
-<p>
-    This message was sent to the following recipients:
-<ul>
-#foreach($recipient in $recipients)
-  <li>$recipient.getPlainAttrMap().get("email").getValues().get(0)</li>
-#end
-</ul>
-
-because one of the following events occurred:
-<ul>
-#foreach($event in $events)
-  <li>$event</i>
-#end
-</ul>
-</p>
-
-#if(!$user.getMemberships().isEmpty())
-You have been provided with the following groups:
-<ul>
-#foreach($membership in $user.getMemberships())
-  <li>$membership.groupName</i>
-#end
-</ul>
-#end
-
-#if(${output.class.simpleName} == "TaskExec")
-Below you can read execution details of task $output.getTask().getClass().getSimpleName(), id $output.getId().
-Task Details:
-<ul>
-<li>
-START DATE:&nbsp$output.getStartDate()
-</li>
-<li>
-MESSAGE:<br/>
-$output.getMessage()
-</li>
-<li>
-END DATE:&nbsp$output.getEndDate()
-</li>
-</ul>
-#end
-
-</body>
-</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/fa28888f/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
new file mode 100644
index 0000000..c8fee4e
--- /dev/null
+++ b/core/provisioning-java/src/main/resources/mailTemplates/optin.txt.jexl3
@@ -0,0 +1,38 @@
+## 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}
+$$ }
+$$ }

http://git-wip-us.apache.org/repos/asf/syncope/blob/fa28888f/core/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm b/core/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm
deleted file mode 100644
index c2b211e..0000000
--- a/core/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm
+++ /dev/null
@@ -1,51 +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.getPlainAttrMap().get("firstname").getValues().get(0) $user.getPlainAttrMap().get("surname").getValues().get(0), welcome to Syncope!
-
-Your username is $user.getUsername().
-Your email address is $user.getPlainAttrMap().get("email").getValues().get(0).
-Your email address inside a link: http://localhost/?email=$esc.url($user.getPlainAttrMap().get("email").getValues().get(0)) .
-
-This message was sent to the following recipients:
-#foreach($recipient in $recipients)
-   * $recipient.getPlainAttrMap().get("surname").getValues().get(0)
-#end
-
-because one of the following events occurred:
-#foreach($event in $events)
-  * $event
-#end
-
-#if(!$user.getMemberships().isEmpty())
-You have been provided with the following groups:
-#foreach($membership in $user.getMemberships())
-  * $membership.groupName
-#end
-#end
-
-#if(${output.class.simpleName} == "TaskExec")
-Below you can read execution details of task $output.getTask().getClass().getSimpleName(), id $output.getId()
-
-Task Details:
-
-  * START DATE: $output.getStartDate() 
-
-  * MESSAGE:
-$output.getMessage()
-
-  * END DATE: $output.getEndDate()
-#end

http://git-wip-us.apache.org/repos/asf/syncope/blob/fa28888f/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
new file mode 100644
index 0000000..0c0428d
--- /dev/null
+++ b/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.jexl3
@@ -0,0 +1,31 @@
+/*
+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/fa28888f/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.vm
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.vm b/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.vm
deleted file mode 100644
index 6594c3f..0000000
--- a/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.vm
+++ /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)">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/fa28888f/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
new file mode 100644
index 0000000..4778884
--- /dev/null
+++ b/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.jexl3
@@ -0,0 +1,26 @@
+## 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/fa28888f/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.vm
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.vm b/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.vm
deleted file mode 100644
index 5ac028a..0000000
--- a/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.vm
+++ /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)
-
-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/fa28888f/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 333f202..8f4dee9 100644
--- a/core/provisioning-java/src/main/resources/provisioningContext.xml
+++ b/core/provisioning-java/src/main/resources/provisioningContext.xml
@@ -66,7 +66,7 @@ under the License.
   </bean>
 
   <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"
-      lazy-init="false" depends-on="quartzDataSourceInit">
+        lazy-init="false" depends-on="quartzDataSourceInit">
     <property name="autoStartup" value="true"/>
     <property name="applicationContextSchedulerContextKey" value="applicationContext"/>
     <property name="waitForJobsToCompleteOnShutdown" value="true"/>
@@ -118,19 +118,10 @@ under the License.
     <constructor-arg value="5000"/>
   </bean>
 
-  <bean id="velocityResourceLoader" class="org.apache.syncope.core.misc.spring.ResourceWithFallbackLoader">
+  <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="velocityEngine" class="org.apache.syncope.core.provisioning.java.notification.VelocityEngineFactoryBean">
-    <property name="resourceLoader" ref="velocityResourceLoader"/>
-  </bean>
-  <bean id="velocityToolManager" class="org.apache.velocity.tools.ToolManager">
-    <!-- autoConfigure -->
-    <constructor-arg index="0" value="true"/>
-    <!-- include default velocity tools -->
-    <constructor-arg index="1" value="true"/>
-  </bean>
 
   <bean id="connIdBundleManager" class="org.apache.syncope.core.provisioning.java.ConnIdBundleManagerImpl">
     <property name="stringLocations" value="${connid.locations}"/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/fa28888f/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
new file mode 100644
index 0000000..d123792
--- /dev/null
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/MailTemplateTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.provisioning.java;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+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.junit.Test;
+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);
+
+        StringWriter writer = new StringWriter();
+        JexlUtils.newJxltEngine().
+                createTemplate(IOUtils.toString(templateResource.getInputStream())).
+                evaluate(new MapContext(jexlVars), writer);
+        return writer.toString();
+    }
+
+    @Test
+    public void confirmPasswordReset() throws IOException {
+        String htmlBody = evaluate(
+                NotificationManagerImpl.MAIL_TEMPLATES
+                + "confirmPasswordReset"
+                + NotificationManagerImpl.MAIL_TEMPLATE_HTML_SUFFIX,
+                new HashMap<String, Object>());
+
+        assertNotNull(htmlBody);
+    }
+
+    @Test
+    public void requestPasswordReset() throws IOException {
+        Map<String, Object> ctx = new HashMap<>();
+
+        String username = "test" + UUID.randomUUID().toString();
+        UserTO user = new UserTO();
+        user.setUsername(username);
+        ctx.put("user", user);
+
+        String token = "token " + UUID.randomUUID().toString();
+        List<String> input = new ArrayList<>();
+        input.add(token);
+        ctx.put("input", input);
+
+        String htmlBody = evaluate(
+                NotificationManagerImpl.MAIL_TEMPLATES
+                + "requestPasswordReset"
+                + NotificationManagerImpl.MAIL_TEMPLATE_HTML_SUFFIX,
+                ctx);
+
+        assertNotNull(htmlBody);
+        assertTrue(htmlBody.contains("a password reset was request for " + username + "."));
+        assertFalse(htmlBody.contains(
+                "http://localhost:9080/syncope-console/?pwdResetToken=" + token));
+        assertTrue(htmlBody.contains(
+                "http://localhost:9080/syncope-console/?pwdResetToken=" + token.replaceAll(" ", "%20")));
+    }
+
+    @Test
+    public void optin() throws IOException {
+        Map<String, Object> ctx = new HashMap<>();
+
+        String username = "test" + UUID.randomUUID().toString();
+        UserTO user = new UserTO();
+        user.setUsername(username);
+        user.getPlainAttrs().add(new AttrTO.Builder().schema("firstname").value("John").build());
+        user.getPlainAttrs().add(new AttrTO.Builder().schema("surname").value("Doe").build());
+        user.getPlainAttrs().add(new AttrTO.Builder().schema("email").value("john.doe@syncope.apache.org").build());
+        user.getMemberships().add(new MembershipTO.Builder().group(23, "a group").build());
+        ctx.put("user", user);
+
+        String token = "token " + UUID.randomUUID().toString();
+        List<String> input = new ArrayList<>();
+        input.add(token);
+        ctx.put("input", input);
+
+        UserTO recipient = SerializationUtils.clone(user);
+        recipient.getPlainAttrMap().get("email").getValues().set(0, "another@syncope.apache.org");
+        ctx.put("recipients", Collections.singletonList(recipient));
+
+        String htmlBody = evaluate(
+                NotificationManagerImpl.MAIL_TEMPLATES
+                + "optin"
+                + NotificationManagerImpl.MAIL_TEMPLATE_HTML_SUFFIX,
+                ctx);
+
+        assertNotNull(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>"));
+        assertTrue(htmlBody.contains("<li>a group</li>"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/fa28888f/deb/core/pom.xml
----------------------------------------------------------------------
diff --git a/deb/core/pom.xml b/deb/core/pom.xml
index 137a81a..8be896ba 100644
--- a/deb/core/pom.xml
+++ b/deb/core/pom.xml
@@ -140,7 +140,7 @@ under the License.
         <includes>
           <include>mail.properties</include>
           <include>connid.properties</include>
-          <include>mailTemplates/*.vm</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/fa28888f/deb/core/src/deb/control/conffiles
----------------------------------------------------------------------
diff --git a/deb/core/src/deb/control/conffiles b/deb/core/src/deb/control/conffiles
index 3bdc093..38d5f12 100644
--- a/deb/core/src/deb/control/conffiles
+++ b/deb/core/src/deb/control/conffiles
@@ -12,9 +12,9 @@
 /etc/apache-syncope/userRoutes.xml
 /etc/apache-syncope/userWorkflow.bpmn20.xml
 /etc/apache-syncope/workflow.properties
-/etc/apache-syncope/mailTemplates/confirmPasswordReset.html.vm
-/etc/apache-syncope/mailTemplates/confirmPasswordReset.txt.vm
-/etc/apache-syncope/mailTemplates/optin.html.vm
-/etc/apache-syncope/mailTemplates/optin.txt.vm
-/etc/apache-syncope/mailTemplates/requestPasswordReset.html.vm
-/etc/apache-syncope/mailTemplates/requestPasswordReset.txt.vm
+/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/fa28888f/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java
index 605663f..a08d1fd 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java
@@ -154,7 +154,7 @@ public class NotificationTaskITCase extends AbstractTaskITCase {
                 taskTO.getTextBody().contains("Your email address is " + recipient + "."));
         assertTrue("Notification mail text doesn't contain expected content.",
                 taskTO.getTextBody().contains("Your email address inside a link: "
-                        + "http://localhost/?email=" + recipient.replaceAll("@", "%40") + " ."));
+                        + "http://localhost/?email=" + recipient.replaceAll("@", "%40")));
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/syncope/blob/fa28888f/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index dea1c5f..12f9354 100644
--- a/pom.xml
+++ b/pom.xml
@@ -763,35 +763,6 @@ under the License.
       </dependency>
       
       <dependency>
-        <groupId>org.apache.velocity</groupId>
-        <artifactId>velocity</artifactId>
-        <version>1.7</version>
-      </dependency>
-      <dependency>
-        <groupId>org.apache.velocity</groupId>
-        <artifactId>velocity-tools</artifactId>
-        <version>2.0</version>
-        <exclusions>
-          <exclusion>
-            <artifactId>struts-core</artifactId>
-            <groupId>org.apache.struts</groupId>
-          </exclusion>
-          <exclusion>
-            <artifactId>struts-taglib</artifactId>
-            <groupId>org.apache.struts</groupId>
-          </exclusion>
-          <exclusion>
-            <artifactId>struts-tiles</artifactId>
-            <groupId>org.apache.struts</groupId>
-          </exclusion>
-          <exclusion>
-            <artifactId>sslext</artifactId>
-            <groupId>sslext</groupId>
-          </exclusion>
-        </exclusions>
-      </dependency>
-      
-      <dependency>
         <groupId>org.apache.cocoon.sax</groupId>
         <artifactId>cocoon-sax</artifactId>
         <version>${cocoon.version}</version>