Testing mail with jetty jndi.

Change-Id: If4393b189e1eaa4380cbccfc8e126e336d122945
diff --git a/core/pom.xml b/core/pom.xml
index d110402..a8420e4 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -10,7 +10,7 @@
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 		<spring-framework.version>5.0.3.RELEASE</spring-framework.version>
 		<jersey.version>1.19.4</jersey.version>
-		<jetty.version>8.2.0.v20160908</jetty.version>
+		<jetty.version>9.4.8.v20171121</jetty.version>
 		<hibernate.version>5.1.11.Final</hibernate.version>
 	</properties>
 	<build>
@@ -358,6 +358,8 @@
 			<artifactId>commons-collections</artifactId>
 			<version>3.2.2</version>
 		</dependency>
+		
+		<!-- jetty -->
 		<dependency>
 			<groupId>org.eclipse.jetty</groupId>
 			<artifactId>jetty-server</artifactId>
@@ -369,6 +371,12 @@
 			<version>${jetty.version}</version>
 		</dependency>
 		<dependency>
+			<groupId>org.eclipse.jetty</groupId>
+			<artifactId>jetty-webapp</artifactId>
+			<version>${jetty.version}</version>
+		</dependency>
+		
+		<dependency>
 			<groupId>asm</groupId>
 			<artifactId>asm</artifactId>
 			<version>3.3.1</version>
diff --git a/core/src/main/java/de/ids_mannheim/korap/user/User.java b/core/src/main/java/de/ids_mannheim/korap/user/User.java
index 8732a21..3cba4a2 100644
--- a/core/src/main/java/de/ids_mannheim/korap/user/User.java
+++ b/core/src/main/java/de/ids_mannheim/korap/user/User.java
@@ -24,9 +24,10 @@
 @Data
 public abstract class User implements Serializable {
 
-//    public static final int ADMINISTRATOR_ID = 34349733;
-//    public static final String ADMINISTRATOR_NAME = "admin";
-
+    //EM: add
+    private String email;
+    //EM: finish
+    
     private Integer id;
     // in local its username, in shib it's edupersonPrincipalName
     private String username;
diff --git a/core/src/main/java/de/ids_mannheim/korap/web/KustvaktBaseServer.java b/core/src/main/java/de/ids_mannheim/korap/web/KustvaktBaseServer.java
index 7ad0500..39e9961 100644
--- a/core/src/main/java/de/ids_mannheim/korap/web/KustvaktBaseServer.java
+++ b/core/src/main/java/de/ids_mannheim/korap/web/KustvaktBaseServer.java
@@ -1,17 +1,22 @@
 package de.ids_mannheim.korap.web;
 
+import javax.naming.InitialContext;
 import javax.servlet.ServletContextListener;
 
 import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.bio.SocketConnector;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.HandlerList;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.webapp.Configuration.ClassList;
+import org.springframework.stereotype.Component;
 import org.springframework.web.context.ContextLoaderListener;
 
 import com.sun.jersey.spi.spring.container.servlet.SpringServlet;
 
-import de.ids_mannheim.korap.config.BeansFactory;
 import de.ids_mannheim.korap.config.KustvaktConfiguration;
 import lombok.Getter;
 import lombok.Setter;
@@ -20,11 +25,14 @@
  * @author hanl
  * @date 01/06/2015
  */
+@Component
 public abstract class KustvaktBaseServer {
+
+    protected static KustvaktConfiguration config;
     
     protected static String rootPackages;
     protected static KustvaktArgs kargs;
-    
+
     public KustvaktBaseServer () {
         KustvaktConfiguration.loadLogger();
     }
@@ -59,51 +67,67 @@
         return kargs;
     }
 
-    protected abstract void setup ();
+    protected void setupJndi (Server server, WebAppContext webapp){}
 
     protected void start(){
-        KustvaktConfiguration config = BeansFactory.getKustvaktContext().getConfiguration();
         
-        if (kargs.init){
-            setup();
-        }
         if (kargs.port == -1){
             kargs.setPort(config.getPort());
         }
-        
+
         Server server = new Server();
+        
         ServletContextHandler contextHandler = new ServletContextHandler(
                 ServletContextHandler.NO_SESSIONS);
         contextHandler.setContextPath("/");
-        contextHandler.setInitParameter("contextConfigLocation", "classpath:"+kargs.getConfig());
+        contextHandler.setInitParameter("contextConfigLocation",
+                "classpath:" + kargs.getConfig());
         
         ServletContextListener listener = new ContextLoaderListener();
         contextHandler.addEventListener(listener);
 
         ServletHolder servletHolder = new ServletHolder(new SpringServlet());
-        servletHolder.setInitParameter("com.sun.jersey.config.property.packages", 
-                rootPackages);
+        servletHolder.setInitParameter(
+                "com.sun.jersey.config.property.packages", rootPackages);
         servletHolder.setInitOrder(1);
         contextHandler.addServlet(servletHolder, config.getBaseURL());
-        
-        SocketConnector connector = new SocketConnector();
+
+        ServerConnector connector = new ServerConnector(server);
         connector.setPort(kargs.port);
-        connector.setMaxIdleTime(60000);
+        connector.setIdleTimeout(60000);
         
         server.setHandler(contextHandler);
+        
+//        ClassList classlist = ClassList.setServerDefault(server);
+//        classlist.addAfter("org.eclipse.jetty.webapp.FragmentConfiguration",
+//                "org.eclipse.jetty.plus.webapp.EnvConfiguration",
+//                "org.eclipse.jetty.plus.webapp.PlusConfiguration");
+        
+//        WebAppContext webapp = new WebAppContext();
+//        webapp.setDescriptor(KustvaktBaseServer.class.getResource("/WEB-INF/web.xml").toString());
+//        webapp.setContextPath("/");
+//        webapp.setWar("./kustvakt.war");
+//        server.setHandler(webapp);
+//        setupJndi(server, webapp);
+//        HandlerList handlerList = new HandlerList();
+//        handlerList.setHandlers(new Handler[]{webapp,contextHandler});
+//        server.setHandler(handlerList);
+        
         server.setConnectors(new Connector[] { connector });
         try {
             server.start();
+//            InitialContext ic = new InitialContext();
+//            Object object = ic.lookup("mail/Session");
             server.join();
         }
         catch (Exception e) {
             System.out.println("Server could not be started!");
             System.out.println(e.getMessage());
+            e.printStackTrace();
             System.exit(-1);
-//            e.printStackTrace();
         }
     }
-    
+
     @Setter
     public static class KustvaktArgs {
 
diff --git a/full/pom.xml b/full/pom.xml
index a65127c..df0d2a8 100644
--- a/full/pom.xml
+++ b/full/pom.xml
@@ -9,6 +9,7 @@
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 		<spring-framework.version>5.0.3.RELEASE</spring-framework.version>
 		<spring-security.version>4.2.3.RELEASE</spring-security.version>
+		<jetty.version>9.4.8.v20171121</jetty.version>
 		<jersey.version>1.19.4</jersey.version>
 		<hibernate.version>5.1.11.Final</hibernate.version>
 	</properties>
@@ -72,10 +73,9 @@
 					<compilerVersion>${java.version}</compilerVersion>
 					<source>${java.version}</source>
 					<target>${java.version}</target>
-					<!-- <compilerArguments>
-						<processor>lombok.launch.AnnotationProcessorHider$AnnotationProcessor</processor>
-						<processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
-					</compilerArguments> -->
+					<!-- <compilerArguments> <processor>lombok.launch.AnnotationProcessorHider$AnnotationProcessor</processor> 
+						<processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor> 
+						</compilerArguments> -->
 					<processors>
 						<processor>lombok.launch.AnnotationProcessorHider$AnnotationProcessor</processor>
 						<processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
@@ -183,7 +183,7 @@
 			<exclusions>
 				<exclusion>
 					<groupId>org.javassist</groupId>
-		   			<artifactId>javassist</artifactId>
+					<artifactId>javassist</artifactId>
 				</exclusion>
 			</exclusions>
 		</dependency>
@@ -194,14 +194,14 @@
 			<scope>provided</scope>
 		</dependency>
 		<dependency>
-		    <groupId>org.hibernate</groupId>
-		    <artifactId>hibernate-java8</artifactId>
-		    <version>${hibernate.version}</version>
+			<groupId>org.hibernate</groupId>
+			<artifactId>hibernate-java8</artifactId>
+			<version>${hibernate.version}</version>
 		</dependency>
 		<dependency>
-		    <groupId>org.javassist</groupId>
-		    <artifactId>javassist</artifactId>
-		    <version>3.22.0-GA</version>
+			<groupId>org.javassist</groupId>
+			<artifactId>javassist</artifactId>
+			<version>3.22.0-GA</version>
 		</dependency>
 
 		<!-- MySql -->
@@ -237,14 +237,55 @@
 			<version>${spring-framework.version}</version>
 		</dependency>
 		<dependency>
-		    <groupId>org.springframework.security</groupId>
-		    <artifactId>spring-security-core</artifactId>
-		    <version>${spring-security.version}</version>
+			<groupId>org.springframework.security</groupId>
+			<artifactId>spring-security-core</artifactId>
+			<version>${spring-security.version}</version>
 		</dependency>
 		<dependency>
-		    <groupId>org.springframework.security</groupId>
-		    <artifactId>spring-security-web</artifactId>
-		    <version>${spring-security.version}</version>
+			<groupId>org.springframework.security</groupId>
+			<artifactId>spring-security-web</artifactId>
+			<version>${spring-security.version}</version>
+		</dependency>
+
+		<!-- jetty -->
+		<dependency>
+			<groupId>org.eclipse.jetty</groupId>
+			<artifactId>jetty-jndi</artifactId>
+			<version>${jetty.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.eclipse.jetty</groupId>
+			<artifactId>jetty-plus</artifactId>
+			<version>${jetty.version}</version>
+		</dependency>
+
+		<!-- velocity -->
+		<dependency>
+			<groupId>org.apache.velocity</groupId>
+			<artifactId>velocity-engine-core</artifactId>
+			<version>2.0</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.velocity</groupId>
+			<artifactId>velocity-tools</artifactId>
+			<version>2.0</version>
+		</dependency>
+		<!-- mail -->
+		<dependency>
+			<groupId>com.sun.mail</groupId>
+			<artifactId>javax.mail</artifactId>
+			<version>1.6.0</version>
+		</dependency>
+		<dependency>
+			<groupId>javax.activation</groupId>
+			<artifactId>activation</artifactId>
+			<version>1.1.1</version>
+		</dependency>
+		
+		<dependency>
+		    <groupId>javax.servlet</groupId>
+		    <artifactId>javax.servlet-api</artifactId>
+		    <version>4.0.0</version>
 		</dependency>
 
 		<!-- Flyway -->
@@ -254,4 +295,5 @@
 			<version>4.0</version>
 		</dependency>
 	</dependencies>
+
 </project>
\ No newline at end of file
diff --git a/full/src/main/java/de/ids_mannheim/korap/authentication/KustvaktAuthenticationManager.java b/full/src/main/java/de/ids_mannheim/korap/authentication/KustvaktAuthenticationManager.java
index 46649eb..564832c 100644
--- a/full/src/main/java/de/ids_mannheim/korap/authentication/KustvaktAuthenticationManager.java
+++ b/full/src/main/java/de/ids_mannheim/korap/authentication/KustvaktAuthenticationManager.java
@@ -139,6 +139,8 @@
 		//EM:copied from EntityDao
 		KorAPUser user = new KorAPUser(); // oder eigentlich new DemoUser oder new DefaultUser.
         user.setUsername(username);
+        // get user data
+        user.setEmail(config.getTestEmail());
         return user;
 //		return entHandler.getAccount(username);
 	}
@@ -215,9 +217,9 @@
 	     	return;
 	    }
 		
-		if (headerMap != null && headerMap.containsKey(org.eclipse.jetty.http.HttpHeaders.X_FORWARDED_FOR)) {
+		if (headerMap != null && headerMap.containsKey(com.google.common.net.HttpHeaders.X_FORWARDED_FOR)) {
 
-			String[] vals = headerMap.getFirst(org.eclipse.jetty.http.HttpHeaders.X_FORWARDED_FOR).split(",");
+			String[] vals = headerMap.getFirst(com.google.common.net.HttpHeaders.X_FORWARDED_FOR).split(",");
 			String clientAddress = vals[0];
 
 			try {
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/FullConfiguration.java b/full/src/main/java/de/ids_mannheim/korap/config/FullConfiguration.java
index 7b9c48c..ad6aaab 100644
--- a/full/src/main/java/de/ids_mannheim/korap/config/FullConfiguration.java
+++ b/full/src/main/java/de/ids_mannheim/korap/config/FullConfiguration.java
@@ -14,6 +14,13 @@
  */
 
 public class FullConfiguration extends KustvaktConfiguration {
+    // mail configuration
+    private boolean isMailEnabled;
+    private String testEmail;
+    private String mailUsername;
+    private String mailPassword;
+    private String mailSmtp;
+    private String mailPort;
 
     private String ldapConfig;
 
@@ -49,12 +56,28 @@
         // EM: pattern for matching availability in Krill matches
         setLicensePatterns(properties);
         setDeleteConfiguration(properties);
+        setMailConfiguration(properties);
         ldapConfig = properties.getProperty("ldap.config");
+
+    }
+
+    private void setMailConfiguration (Properties properties) {
+        setMailEnabled(Boolean.valueOf(properties.getProperty("mail.enabled", "false")));
+        setTestEmail(properties.getProperty("mail.receiver"));
+        if (isMailEnabled){
+            // other properties must be set in the kustvakt.conf
+            setMailUsername(properties.getProperty("mail.username"));
+            setMailPassword(properties.getProperty("mail.password"));
+            setMailSmtp(properties.getProperty("mail.smtp"));
+            setMailPort(properties.getProperty("mail.port"));
+        }
     }
 
     private void setDeleteConfiguration (Properties properties) {
-        setSoftDeleteGroup(parseDeleteConfig(properties.getProperty("delete.group", "")));
-        setSoftDeleteAutoGroup(parseDeleteConfig(properties.getProperty("delete.auto.group", "")));
+        setSoftDeleteGroup(
+                parseDeleteConfig(properties.getProperty("delete.group", "")));
+        setSoftDeleteAutoGroup(parseDeleteConfig(
+                properties.getProperty("delete.auto.group", "")));
         setSoftDeleteGroupMember(parseDeleteConfig(
                 properties.getProperty("delete.group.member", "")));
     }
@@ -217,4 +240,52 @@
         this.isSoftDeleteAutoGroup = isSoftDeleteAutoGroup;
     }
 
+    public String getTestEmail () {
+        return testEmail;
+    }
+
+    public void setTestEmail (String testEmail) {
+        this.testEmail = testEmail;
+    }
+
+    public String getMailUsername () {
+        return mailUsername;
+    }
+
+    public void setMailUsername (String mailUsername) {
+        this.mailUsername = mailUsername;
+    }
+
+    public String getMailPassword () {
+        return mailPassword;
+    }
+
+    public void setMailPassword (String mailPassword) {
+        this.mailPassword = mailPassword;
+    }
+
+    public String getMailSmtp () {
+        return mailSmtp;
+    }
+
+    public void setMailSmtp (String mailHost) {
+        this.mailSmtp = mailHost;
+    }
+
+    public String getMailPort () {
+        return mailPort;
+    }
+
+    public void setMailPort (String mailPort) {
+        this.mailPort = mailPort;
+    }
+
+    public boolean isMailEnabled () {
+        return isMailEnabled;
+    }
+
+    public void setMailEnabled (boolean isMailEnabled) {
+        this.isMailEnabled = isMailEnabled;
+    }
+
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/server/KustvaktServer.java b/full/src/main/java/de/ids_mannheim/korap/server/KustvaktServer.java
index 4b3a6d6..6871205 100644
--- a/full/src/main/java/de/ids_mannheim/korap/server/KustvaktServer.java
+++ b/full/src/main/java/de/ids_mannheim/korap/server/KustvaktServer.java
@@ -1,6 +1,22 @@
 package de.ids_mannheim.korap.server;
 
-import de.ids_mannheim.korap.config.BeansFactory;
+import java.io.File;
+import java.io.FileInputStream;
+import java.net.URL;
+import java.util.Properties;
+
+import javax.naming.NamingException;
+
+import org.eclipse.jetty.jndi.factories.MailSessionReference;
+import org.eclipse.jetty.plus.jndi.Resource;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.ResourceHandler;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.webapp.Configuration.ClassList;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+import de.ids_mannheim.korap.config.FullConfiguration;
 import de.ids_mannheim.korap.web.KustvaktBaseServer;
 
 /**
@@ -11,17 +27,35 @@
  */
 public class KustvaktServer extends KustvaktBaseServer {
 
+    private static FullConfiguration fullConfig;
+    
     public static final String API_VERSION = "v0.1";
     
     public static void main (String[] args) throws Exception {
         KustvaktServer server = new KustvaktServer();
         kargs = server.readAttributes(args);
+        
+        File f = new File("kustvakt.conf");
+        if (!f.exists()){
+            URL url = KustvaktServer.class.getClassLoader().getResource("kustvakt.conf");
+            if (url!=null){
+                f = new File(url.toURI());
+            }
+        }
+        
+        Properties properties = new Properties();
+        FileInputStream in = new FileInputStream(f);
+        properties.load(in);
+        in.close();
+        fullConfig = new FullConfiguration(properties);
+        config = fullConfig;
 
-        if (kargs.getConfig() != null)
-            BeansFactory.loadFileContext(kargs.getConfig());
-        else{
+        if (kargs.getConfig() == null){
+//            BeansFactory.loadFileContext(kargs.getConfig());
+//        }
+//        else {
             kargs.setConfig("default-config.xml");
-            BeansFactory.loadClasspathContext("default-config.xml");
+//            BeansFactory.loadClasspathContext("default-config.xml");
         }
         kargs.setRootPackages(new String[] { "de.ids_mannheim.korap.web.utils",
                 "de.ids_mannheim.korap.web.service.full" });
@@ -31,52 +65,30 @@
     }
 
     @Override
-    protected void setup () {
-//        Set<Class<? extends BootableBeanInterface>> set = KustvaktClassLoader
-//                .loadSubTypes(BootableBeanInterface.class);
-//
-//        ContextHolder context = BeansFactory.getKustvaktContext();
-//        if (context == null)
-//            throw new RuntimeException("Beans could not be loaded!");
-//
-//        List<BootableBeanInterface> list = new ArrayList<>(set.size());
-//        for (Class cl : set) {
-//            BootableBeanInterface iface;
-//            
-//            try {
-//                iface = (BootableBeanInterface) cl.newInstance();
-//                if (iface instanceof CollectionLoader){
-//                	continue;
-//                }
-//                list.add(iface);
-//            }
-//            catch (InstantiationException | IllegalAccessException e) {
-//                continue;
-//            }
-//        }
-//        System.out.println("Found boot loading interfaces: " + list);
-//
-//        while (!list.isEmpty()) {
-//            loop: for (BootableBeanInterface iface : new ArrayList<>(list)) {
-//                try {
-//                    for (Class dep : iface.getDependencies()) {
-//                        if (set.contains(dep))
-//                            continue loop;
-//                    }
-//                    iface.load(context);
-//                    list.remove(iface);
-//                    set.remove(iface.getClass());
-//                    System.out.println("Done with interface "
-//                            + iface.getClass().getSimpleName());
-//                }
-//                catch (KustvaktException e) {
-//                    // don't do anything!
-//                    System.out.println("An error occurred in class "
-//                            + iface.getClass().getSimpleName() + "!\n");
-//                    e.printStackTrace();
-//                    System.exit(-1);
-//                }
-//            }
-//        }
+    protected void setupJndi(Server server, WebAppContext webapp) {
+        
+        
+//        ClassList classlist = ClassList.setServerDefault(server);
+//        classlist.addAfter("org.eclipse.jetty.webapp.FragmentConfiguration",
+//                "org.eclipse.jetty.plus.webapp.EnvConfiguration",
+//                "org.eclipse.jetty.plus.webapp.PlusConfiguration");
+        
+        MailSessionReference mailref = new MailSessionReference();
+        mailref.setUser(fullConfig.getMailUsername());
+        mailref.setPassword(fullConfig.getMailPassword());
+        
+        Properties props = new Properties();
+        props.put("mail.smtp.auth", "false");
+        props.put("mail.smtp.host",fullConfig.getMailSmtp());
+        props.put("mail.from",fullConfig.getMailUsername());
+        props.put("mail.debug", "false");
+        mailref.setProperties(props);
+        try {
+            new Resource(webapp, "mail/Session", mailref);
+        }
+        catch (NamingException e) {
+            e.printStackTrace();
+        }
     }
+    
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/MailService.java b/full/src/main/java/de/ids_mannheim/korap/service/MailService.java
new file mode 100644
index 0000000..74164d5
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/service/MailService.java
@@ -0,0 +1,63 @@
+package de.ids_mannheim.korap.service;
+
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.context.Context;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.mail.javamail.MimeMessagePreparator;
+import org.springframework.stereotype.Service;
+
+import de.ids_mannheim.korap.interfaces.AuthenticationManagerIface;
+import de.ids_mannheim.korap.user.User;
+
+@Service
+public class MailService {
+    @Autowired
+    private AuthenticationManagerIface authManager;
+//    @Autowired
+    private JavaMailSender mailSender;
+//    @Autowired
+    private VelocityEngine velocityEngine;
+
+    public void sendMemberInvitationNotification (String inviteeName,
+            String groupName, String inviter) {
+
+        MimeMessagePreparator preparator = new MimeMessagePreparator() {
+
+            public void prepare (MimeMessage mimeMessage) throws Exception {
+
+                User invitee = authManager.getUser(inviteeName);
+
+                MimeMessageHelper message = new MimeMessageHelper(mimeMessage);
+                message.setTo(new InternetAddress(invitee.getEmail()));
+                message.setFrom("noreply-korap-notification@ids-mannheim.de");
+                message.setSubject("Invitation to join group");
+                message.setText(prepareText(inviteeName, groupName, inviter),
+                        true);
+            }
+
+        };
+        mailSender.send(preparator);
+    }
+
+    private String prepareText (String username, String groupName,
+            String inviter) {
+        Context context = new VelocityContext();
+        context.put("username", username);
+        context.put("groupName", groupName);
+        context.put("inviter", inviter);
+
+        StringWriter stringWriter = new StringWriter();
+        velocityEngine.mergeTemplate("invitationNotification.vm",
+                StandardCharsets.UTF_16.name(), context, stringWriter);
+        return stringWriter.toString();
+    }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/UserGroupService.java b/full/src/main/java/de/ids_mannheim/korap/service/UserGroupService.java
index 1dfd16f..e05053e 100644
--- a/full/src/main/java/de/ids_mannheim/korap/service/UserGroupService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/service/UserGroupService.java
@@ -1,11 +1,8 @@
 package de.ids_mannheim.korap.service;
 
-import java.time.LocalDateTime;
-import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Date;
 import java.util.List;
 
 import org.slf4j.Logger;
@@ -57,6 +54,8 @@
     private AuthenticationManagerIface authManager;
     @Autowired
     private FullConfiguration config;
+    @Autowired
+    private MailService mailService;
 
     private static List<Role> memberRoles;
 
@@ -176,7 +175,7 @@
                 // skip owner, already added while creating group.
                 continue;
             }
-            addGroupMember(memberId, userGroup, createdBy,
+            inviteGroupMember(memberId, userGroup, createdBy,
                     GroupMemberStatus.PENDING);
         }
     }
@@ -226,7 +225,7 @@
      * @param status the status of the membership
      * @throws KustvaktException
      */
-    public void addGroupMember (String username, UserGroup userGroup,
+    public void inviteGroupMember (String username, UserGroup userGroup,
             String createdBy, GroupMemberStatus status)
             throws KustvaktException {
 
@@ -249,8 +248,12 @@
         member.setRoles(memberRoles);
         member.setStatus(status);
         member.setUserId(username);
-
         groupMemberDao.addMember(member);
+
+        if (config.isMailEnabled()) {
+            mailService.sendMemberInvitationNotification(username,
+                    userGroup.getName(), createdBy);
+        }
     }
 
     private boolean memberExists (String username, int groupId,
@@ -277,7 +280,7 @@
         return false;
     }
 
-    public void addUsersToGroup (UserGroupJson group, String username)
+    public void inviteGroupMembers (UserGroupJson group, String inviter)
             throws KustvaktException {
         int groupId = group.getId();
         String[] members = group.getMembers();
@@ -285,16 +288,16 @@
         ParameterChecker.checkObjectValue(members, "members");
 
         UserGroup userGroup = retrieveUserGroupById(groupId);
-        User user = authManager.getUser(username);
-        if (isUserGroupAdmin(username, userGroup) || user.isSystemAdmin()) {
+        User user = authManager.getUser(inviter);
+        if (isUserGroupAdmin(inviter, userGroup) || user.isSystemAdmin()) {
             for (String memberName : members) {
-                addGroupMember(memberName, userGroup, username,
+                inviteGroupMember(memberName, userGroup, inviter,
                         GroupMemberStatus.PENDING);
             }
         }
         else {
             throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
-                    "Unauthorized operation for user: " + username, username);
+                    "Unauthorized operation for user: " + inviter, inviter);
         }
     }
 
@@ -322,9 +325,9 @@
 
         ParameterChecker.checkStringValue(username, "userId");
         ParameterChecker.checkIntegerValue(groupId, "groupId");
-        
+
         UserGroup group = userGroupDao.retrieveGroupById(groupId);
-        
+
         UserGroupMember member =
                 groupMemberDao.retrieveMemberById(username, groupId);
         GroupMemberStatus status = member.getStatus();
@@ -337,22 +340,21 @@
         else if (member.getStatus().equals(GroupMemberStatus.ACTIVE)) {
             throw new KustvaktException(StatusCodes.GROUP_MEMBER_EXISTS,
                     "Username " + username + " with status " + status
-                            + " exists in the user-group "
-                            + group.getName(),
+                            + " exists in the user-group " + group.getName(),
                     username, status.name(), group.getName());
         }
         // status pending
         else {
-            jlog.debug("status: " +member.getStatusDate());
+            jlog.debug("status: " + member.getStatusDate());
             ZonedDateTime expiration = member.getStatusDate().plusMinutes(30);
             ZonedDateTime now = ZonedDateTime.now();
             jlog.debug("expiration: " + expiration + ", now: " + now);
 
-            if (expiration.isAfter(now)){
+            if (expiration.isAfter(now)) {
                 member.setStatus(GroupMemberStatus.ACTIVE);
                 groupMemberDao.updateMember(member);
             }
-            else{
+            else {
                 throw new KustvaktException(StatusCodes.INVITATION_EXPIRED);
             }
         }
@@ -405,9 +407,9 @@
      */
     private void deleteMember (String username, int groupId, String deletedBy,
             boolean isSoftDelete) throws KustvaktException {
-        
+
         UserGroup group = userGroupDao.retrieveGroupById(groupId);
-        
+
         UserGroupMember member =
                 groupMemberDao.retrieveMemberById(username, groupId);
         GroupMemberStatus status = member.getStatus();
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java b/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java
index b4e7fbc..4e0214b 100644
--- a/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java
@@ -395,7 +395,7 @@
                         userGroupService.retrieveHiddenGroup(vcId);
                 //                if (!userGroupService.isMember(username, userGroup)) {
                 try {
-                    userGroupService.addGroupMember(username, userGroup,
+                    userGroupService.inviteGroupMember(username, userGroup,
                             "system", GroupMemberStatus.ACTIVE);
                 }
                 catch (KustvaktException e) {
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
index eda4eeb..9e15d33 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
@@ -166,12 +166,12 @@
     @POST
     @Path("member/invite")
     @Consumes(MediaType.APPLICATION_JSON)
-    public Response addUserToGroup (@Context SecurityContext securityContext,
+    public Response inviteGroupMembers (@Context SecurityContext securityContext,
             UserGroupJson group) {
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
-            service.addUsersToGroup(group, context.getUsername());
+            service.inviteGroupMembers(group, context.getUsername());
             return Response.ok().build();
         }
         catch (KustvaktException e) {
diff --git a/full/src/main/resources/WEB-INF/web.xml b/full/src/main/resources/WEB-INF/web.xml
new file mode 100644
index 0000000..4c2a254
--- /dev/null
+++ b/full/src/main/resources/WEB-INF/web.xml
@@ -0,0 +1,5 @@
+<resource-ref>
+  <res-ref-name>mail/Session</res-ref-name>
+  <res-type>javax.mail.Session</res-type>
+  <res-auth>Container</res-auth>
+</resource-ref>
\ No newline at end of file
diff --git a/full/src/main/resources/kustvakt.conf b/full/src/main/resources/kustvakt.conf
index d8ff39d..2e29c02 100644
--- a/full/src/main/resources/kustvakt.conf
+++ b/full/src/main/resources/kustvakt.conf
@@ -10,6 +10,9 @@
 ldap.config = file-path-to-ldap-config
 
 # Kustvakt
+## mail settings
+mail.enabled = false
+
 ## default layers
 default.layer.p = tt
 default.layer.l = tt
diff --git a/full/src/main/resources/templates/invitationNotification.vm b/full/src/main/resources/templates/invitationNotification.vm
new file mode 100644
index 0000000..02cf5c6
--- /dev/null
+++ b/full/src/main/resources/templates/invitationNotification.vm
@@ -0,0 +1,13 @@
+<html>
+	<body>
+		<h3>Hi ${username}, you are invited to group {$group} by ${inviter}!</h3>
+		<p>
+			Login to KorAP to accept or reject the invitation.
+			<br /> 
+			The invitation is valid for 30 minutes.
+		</p>
+		<p>
+			Do not reply! This is an automated generated email.
+		</p>
+	</body>
+</html>
\ No newline at end of file
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/MatchInfoControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/MatchInfoControllerTest.java
index 11a3193..4b0004a 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/MatchInfoControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/MatchInfoControllerTest.java
@@ -4,16 +4,15 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import org.eclipse.jetty.http.HttpHeaders;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.net.HttpHeaders;
 import com.sun.jersey.api.client.ClientResponse;
 
 import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
 import de.ids_mannheim.korap.config.Attributes;
-import de.ids_mannheim.korap.config.TokenType;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.utils.JsonUtils;
 import de.ids_mannheim.korap.web.FastJerseyTest;
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java
index 8d36bd8..b27e1bf 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java
@@ -10,17 +10,16 @@
 
 import javax.ws.rs.core.MediaType;
 
-import org.eclipse.jetty.http.HttpHeaders;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.net.HttpHeaders;
 import com.sun.jersey.api.client.ClientResponse;
 
 import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
 import de.ids_mannheim.korap.config.Attributes;
-import de.ids_mannheim.korap.config.TokenType;
 import de.ids_mannheim.korap.config.ContextHolder;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.interfaces.db.EntityHandlerIface;
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchWithAvailabilityTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchWithAvailabilityTest.java
index 02abd36..00cdd97 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchWithAvailabilityTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchWithAvailabilityTest.java
@@ -3,11 +3,11 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
-import org.eclipse.jetty.http.HttpHeaders;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.net.HttpHeaders;
 import com.sun.jersey.api.client.ClientHandlerException;
 import com.sun.jersey.api.client.ClientResponse;
 import com.sun.jersey.api.client.UniformInterfaceException;
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java
index ce88df3..f5f44cb 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java
@@ -5,11 +5,11 @@
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 
-import org.eclipse.jetty.http.HttpHeaders;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.net.HttpHeaders;
 import com.sun.jersey.api.client.ClientHandlerException;
 import com.sun.jersey.api.client.ClientResponse;
 import com.sun.jersey.api.client.ClientResponse.Status;
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java
index 0df3720..e36fc0f 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java
@@ -15,11 +15,11 @@
 import javax.ws.rs.core.MultivaluedMap;
 
 import org.apache.http.entity.ContentType;
-import org.eclipse.jetty.http.HttpHeaders;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.net.HttpHeaders;
 import com.sun.jersey.api.client.ClientHandlerException;
 import com.sun.jersey.api.client.ClientResponse;
 import com.sun.jersey.api.client.ClientResponse.Status;
diff --git a/full/src/test/resources/kustvakt-test.conf b/full/src/test/resources/kustvakt-test.conf
index 3d5eadd..7b19a51 100644
--- a/full/src/test/resources/kustvakt-test.conf
+++ b/full/src/test/resources/kustvakt-test.conf
@@ -10,6 +10,8 @@
 ldap.config = file-path-to-ldap-config
 
 # Kustvakt
+## mail settings
+mail.enabled = false
 
 ## server
 server.port=8089
diff --git a/full/src/test/resources/test-config.xml b/full/src/test/resources/test-config.xml
index 01f4e75..01a5ba8 100644
--- a/full/src/test/resources/test-config.xml
+++ b/full/src/test/resources/test-config.xml
@@ -76,27 +76,27 @@
 		<property name="username" value="${jdbc.username}" />
 		<property name="password" value="${jdbc.password}" />
 		<property name="connectionProperties">
-            <props>
-                <prop key="date_string_format">yyyy-MM-dd HH:mm:ss</prop>
-            </props>
-        </property>
-        
+			<props>
+				<prop key="date_string_format">yyyy-MM-dd HH:mm:ss</prop>
+			</props>
+		</property>
+
 		<!-- Sqlite can only have a single connection -->
 		<property name="suppressClose">
 			<value>true</value>
 		</property>
 	</bean>
-	
+
 	<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
-	    destroy-method="close">
-	    <property name="driverClass" value="${jdbc.driverClassName}" />
+		destroy-method="close">
+		<property name="driverClass" value="${jdbc.driverClassName}" />
 		<property name="jdbcUrl" value="${jdbc.url}" />
 		<property name="user" value="${jdbc.username}" />
 		<property name="password" value="${jdbc.password}" />
-	    <property name="maxPoolSize" value="4" />
-	    <property name="minPoolSize" value="1" />
-	    <property name="maxStatements" value="1" />
-	    <property name="testConnectionOnCheckout" value="true" />
+		<property name="maxPoolSize" value="4" />
+		<property name="minPoolSize" value="1" />
+		<property name="maxStatements" value="1" />
+		<property name="testConnectionOnCheckout" value="true" />
 	</bean>
 
 	<!-- to configure database for sqlite, mysql, etc. migrations -->
@@ -155,12 +155,12 @@
 	</bean>
 
 	<!-- Data access objects -->
-	<bean id="resourceDao" class="de.ids_mannheim.korap.dao.ResourceDao"/>
+	<bean id="resourceDao" class="de.ids_mannheim.korap.dao.ResourceDao" />
 	<!-- <bean id="annotationDao" class="de.ids_mannheim.korap.dao.AnnotationDao"/> -->
 
 	<!-- Krill -->
 	<bean id="search_krill" class="de.ids_mannheim.korap.web.SearchKrill">
-		<constructor-arg value="${krill.indexDir}"/>
+		<constructor-arg value="${krill.indexDir}" />
 	</bean>
 
 
@@ -171,11 +171,10 @@
 	<bean id="kustvakt_auditing" class="de.ids_mannheim.korap.handlers.JDBCAuditing">
 		<constructor-arg ref="kustvakt_db" />
 	</bean>
-	
-	<bean id="kustvakt_response"
-          class="de.ids_mannheim.korap.web.FullResponseHandler">
-          <constructor-arg index="0" name="iface" ref="kustvakt_auditing"/>
-    </bean>
+
+	<bean id="kustvakt_response" class="de.ids_mannheim.korap.web.FullResponseHandler">
+		<constructor-arg index="0" name="iface" ref="kustvakt_auditing" />
+	</bean>
 
 	<bean id="kustvakt_userdb" class="de.ids_mannheim.korap.handlers.EntityDao">
 		<constructor-arg ref="kustvakt_db" />
@@ -203,15 +202,14 @@
 	</bean>
 
 	<!-- authentication providers to use -->
-	<!-- <bean id="api_auth" class="de.ids_mannheim.korap.authentication.APIAuthentication">
-		<constructor-arg type="de.ids_mannheim.korap.config.KustvaktConfiguration"
-			ref="kustvakt_config" />
-	</bean> -->
+	<!-- <bean id="api_auth" class="de.ids_mannheim.korap.authentication.APIAuthentication"> 
+		<constructor-arg type="de.ids_mannheim.korap.config.KustvaktConfiguration" 
+		ref="kustvakt_config" /> </bean> -->
 	<bean id="ldap_auth" class="de.ids_mannheim.korap.authentication.LdapAuth3">
 		<constructor-arg type="de.ids_mannheim.korap.config.KustvaktConfiguration"
 			ref="kustvakt_config" />
 	</bean>
-	
+
 	<bean id="openid_auth"
 		class="de.ids_mannheim.korap.authentication.OpenIDconnectAuthentication">
 		<constructor-arg type="de.ids_mannheim.korap.config.KustvaktConfiguration"
@@ -220,7 +218,8 @@
 			type="de.ids_mannheim.korap.interfaces.db.PersistenceClient" ref="kustvakt_db" />
 	</bean>
 
-	<bean id="basic_auth" class="de.ids_mannheim.korap.authentication.BasicAuthentication" />
+	<bean id="basic_auth"
+		class="de.ids_mannheim.korap.authentication.BasicAuthentication" />
 
 
 	<bean id="session_auth"
@@ -305,4 +304,21 @@
 		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
 		<property name="dataSource" ref="dataSource" />
 	</bean>
+
+	<!-- mail -->
+	<!-- <bean id="smtpSession" class="org.springframework.jndi.JndiObjectFactoryBean">
+		<property name="jndiName" value="java:comp/env/mail/jetty" />
+	</bean>
+	<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
+		<property name="session" ref="smtpSession" />
+	</bean>
+	<bean id="velocityEngine" class="org.apache.velocity.app.VelocityEngine">
+		<constructor-arg index="0">
+			<props>
+				<prop key="resource.loader">class</prop>
+				<prop key="class.resource.loader.class">org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
+				</prop>
+			</props>
+		</constructor-arg>
+	</bean> -->
 </beans>
\ No newline at end of file
diff --git a/lite/src/main/java/de/ids_mannheim/korap/server/KustvaktLiteServer.java b/lite/src/main/java/de/ids_mannheim/korap/server/KustvaktLiteServer.java
index c0e31f3..acac717 100644
--- a/lite/src/main/java/de/ids_mannheim/korap/server/KustvaktLiteServer.java
+++ b/lite/src/main/java/de/ids_mannheim/korap/server/KustvaktLiteServer.java
@@ -22,10 +22,4 @@
         server.start();
     }
 
-    @Override
-    protected void setup () {
-        // TODO Auto-generated method stub
-
-    }
-
 }