Added delete-config & tests; updated lib & java versions; fixed bugs.

Change-Id: I7a2a7d316b28e856062b678a70d9c0d251ed1ee8
diff --git a/core/Changes b/core/Changes
index c6ff1ba..d1d629e 100644
--- a/core/Changes
+++ b/core/Changes
@@ -1,7 +1,9 @@
-0.59.10 2018-01-25 
+0.59.10 2018-02-01 
 	- updated hibernate and reflection versions (margaretha)
 	- added Changes file (margaretha)
 	- merged BeanConfigBaseTest to BeanConfigTest in /full (margaretha)
+	- added status code for already deleted entry (margaretha)
+	- updated library versions and java environment (margaretha)	
 0.59.9 	2017-11-08
 	- fixed missing exception in JsonUtils (margaretha)
 	- fixed and restructured KustvaktResponseHandler (margaretha)
diff --git a/core/pom.xml b/core/pom.xml
index cb42b84..d110402 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -6,10 +6,11 @@
 	<version>0.59.10</version>
 
 	<properties>
-		<java.version>1.7</java.version>
+		<java.version>1.8</java.version>
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-		<spring-framework.version>4.3.11.RELEASE</spring-framework.version>
+		<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>
 		<hibernate.version>5.1.11.Final</hibernate.version>
 	</properties>
 	<build>
@@ -53,13 +54,13 @@
 			<plugin>
 				<groupId>com.googlecode.maven-java-formatter-plugin</groupId>
 				<artifactId>maven-java-formatter-plugin</artifactId>
-				<version>0.4</version>
+				<version>${project.version}</version>
 				<configuration>
 					<configFile>${project.basedir}/Format.xml</configFile>
 					<overrideConfigCompilerVersion>true</overrideConfigCompilerVersion>
-					<compilerSource>1.7</compilerSource>
-					<compilerCompliance>1.7</compilerCompliance>
-					<compilerTargetPlatform>1.7</compilerTargetPlatform>
+					<compilerSource>${java.version}</compilerSource>
+					<compilerCompliance>${java.version}</compilerCompliance>
+					<compilerTargetPlatform>${java.version}</compilerTargetPlatform>
 				</configuration>
 				<!-- <executions> <execution> <goals> <goal>format</goal> </goals> </execution> 
 					</executions> -->
@@ -67,7 +68,7 @@
 			<plugin>
 				<groupId>org.apache.maven.plugins</groupId>
 				<artifactId>maven-compiler-plugin</artifactId>
-				<version>3.3</version>
+				<version>3.7.0</version>
 				<configuration>
 					<compilerVersion>${java.version}</compilerVersion>
 					<source>${java.version}</source>
@@ -77,10 +78,10 @@
 
 			<!-- build tests jar, so extensions can use fastjerseytest class to build 
 				rest tests -->
-			<plugin>
+			<!-- <plugin>
 				<groupId>org.apache.maven.plugins</groupId>
 				<artifactId>maven-jar-plugin</artifactId>
-				<version>2.1</version>
+				<version>3.0.2</version>
 				<executions>
 					<execution>
 						<phase>package</phase>
@@ -89,7 +90,7 @@
 						</goals>
 					</execution>
 				</executions>
-			</plugin>
+			</plugin> -->
 			<!-- Generate source jar -->
 		    <plugin>
 		      <groupId>org.apache.maven.plugins</groupId>
@@ -192,7 +193,7 @@
 		<dependency>
 			<groupId>org.slf4j</groupId>
 			<artifactId>slf4j-log4j12</artifactId>
-			<version>1.7.5</version>
+			<version>1.7.25</version>
 		</dependency>
 		<!-- <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> 
 			<version>1.2.17</version> </dependency> <dependency> <groupId>log4j</groupId> 
@@ -205,12 +206,12 @@
 		<dependency>
 			<groupId>org.projectlombok</groupId>
 			<artifactId>lombok</artifactId>
-			<version>1.16.6</version>
+			<version>1.16.20</version>
 		</dependency>
 		<dependency>
 			<groupId>joda-time</groupId>
 			<artifactId>joda-time</artifactId>
-			<version>2.2</version>
+			<version>2.9.9</version>
 		</dependency>
 		<dependency>
 			<groupId>de.ids_mannheim.korap</groupId>
@@ -227,7 +228,7 @@
 		<dependency>
 			<groupId>org.xerial</groupId>
 			<artifactId>sqlite-jdbc</artifactId>
-			<version>3.20.1</version>
+			<version>3.21.0</version>
 		</dependency>
 
 		
@@ -235,21 +236,22 @@
 		<dependency>
 			<groupId>org.apache.commons</groupId>
 			<artifactId>commons-dbcp2</artifactId>
-			<version>2.1.1</version>
+			<version>2.2.0</version>
 		</dependency>
 
 		<dependency>
 			<groupId>commons-validator</groupId>
 			<artifactId>commons-validator</artifactId>
-			<version>1.4.0</version>
+			<version>1.6</version>
 		</dependency>
 
 		<dependency>
 			<groupId>org.mindrot</groupId>
 			<artifactId>jbcrypt</artifactId>
-			<version>0.3m</version>
+			<version>0.4</version>
 		</dependency>
-
+		
+		<!-- EM: nimbus version is very old -->
 		<dependency>
 			<groupId>com.nimbusds</groupId>
 			<artifactId>nimbus-jose-jwt</artifactId>
@@ -354,17 +356,17 @@
 		<dependency>
 			<groupId>commons-collections</groupId>
 			<artifactId>commons-collections</artifactId>
-			<version>3.2.1</version>
+			<version>3.2.2</version>
 		</dependency>
 		<dependency>
 			<groupId>org.eclipse.jetty</groupId>
 			<artifactId>jetty-server</artifactId>
-			<version>8.1.8.v20121106</version>
+			<version>${jetty.version}</version>
 		</dependency>
 		<dependency>
 			<groupId>org.eclipse.jetty</groupId>
 			<artifactId>jetty-servlet</artifactId>
-			<version>8.1.8.v20121106</version>
+			<version>${jetty.version}</version>
 		</dependency>
 		<dependency>
 			<groupId>asm</groupId>
@@ -414,12 +416,12 @@
 		<dependency>
 			<groupId>org.apache.httpcomponents</groupId>
 			<artifactId>httpclient</artifactId>
-			<version>4.3.3</version>
+			<version>4.5.4</version>
 		</dependency>
 		<dependency>
 			<groupId>commons-io</groupId>
 			<artifactId>commons-io</artifactId>
-			<version>2.4</version>
+			<version>2.6</version>
 		</dependency>
 
 		<dependency>
diff --git a/core/src/main/java/de/ids_mannheim/korap/exceptions/KustvaktException.java b/core/src/main/java/de/ids_mannheim/korap/exceptions/KustvaktException.java
index 1ca8904..ac73909 100644
--- a/core/src/main/java/de/ids_mannheim/korap/exceptions/KustvaktException.java
+++ b/core/src/main/java/de/ids_mannheim/korap/exceptions/KustvaktException.java
@@ -30,8 +30,10 @@
     }
     
     public KustvaktException (int status, String ... args) {
+        super(args[0]);
         this.statusCode = status;
-        this.entity = Arrays.asList(args).toString();
+        String[] subarray = Arrays.copyOfRange(args, 1, args.length);
+        this.entity = Arrays.asList(subarray).toString();
     }
 
     public KustvaktException (int status, String notification, boolean isNotification) {
diff --git a/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java b/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
index ae96327..5b57438 100644
--- a/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
+++ b/core/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
@@ -15,7 +15,7 @@
      */
     public static final int DEFAULT_ERROR = 100;
     public static final int NO_RESULT_FOUND = 101;
-    public static final int ENTRY_EXISTS = 102;
+
     public static final int UNSUPPORTED_OPERATION = 103;
     public static final int ILLEGAL_ARGUMENT = 104;
     public static final int MISSING_ARGUMENT = 105;
@@ -79,7 +79,9 @@
     public static final int DB_INSERT_SUCCESSFUL = 505;
     public static final int DB_DELETE_SUCCESSFUL = 506;
     public static final int DB_UPDATE_SUCCESSFUL = 507;
-
+    
+    public static final int DB_ENTRY_EXISTS = 508;
+    public static final int DB_ENTRY_DELETED = 509;
 
 //    public static final int ARGUMENT_VALIDATION_FAILURE = 700;
     // public static final int ARGUMENT_VALIDATION_FAILURE = 701;
diff --git a/full/Changes b/full/Changes
index 0a8299a..b147983 100644
--- a/full/Changes
+++ b/full/Changes
@@ -1,4 +1,4 @@
-0.59.10	2018-01-25 
+0.59.10	2018-02-01 
 	- added sort VC by id (margaretha)
 	- added test cases regarding VC sharing (margaretha)
 	- implemented withdraw VC from publication (margaretha)
@@ -6,7 +6,10 @@
 	- implemented add users to group (margaretha)
 	- implemented delete user-group and member tasks (margaretha)
 	- added userMemberStatus in group lists (margaretha)
-	- updated sql test data (margaretha)
+	- updated and added SQL test data (margaretha)
+	- added user group related tests (margaretha)
+	- implemented custom configuration for deleting user groups and members (margaretha)
+	- updated library versions and java environment (margaretha)
 	
 0.59.9 2018-01-19
 	- restructured basic authentication (margaretha)
@@ -51,7 +54,5 @@
 	- fixed missing exceptions in JsonUtils (margaretha)
 	- restructured web filters and authentication codes (margaretha)
 	- implemented create/store VC (margaretha)
-	- fixed collection rewrite bug regarding availability with operation or (margaretha)
-	
-	
+	- fixed collection rewrite bug regarding availability with operation or (margaretha)	
     
\ No newline at end of file
diff --git a/full/pom.xml b/full/pom.xml
index 02fc984..bccc9bf 100644
--- a/full/pom.xml
+++ b/full/pom.xml
@@ -5,9 +5,10 @@
 	<artifactId>Kustvakt-full</artifactId>
 	<version>0.59.10</version>
 	<properties>
-		<java.version>1.7</java.version>
+		<java.version>1.8</java.version>
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-		<spring-framework.version>4.3.11.RELEASE</spring-framework.version>
+		<spring-framework.version>5.0.3.RELEASE</spring-framework.version>
+		<spring-security.version>4.2.3.RELEASE</spring-security.version>
 		<jersey.version>1.19.4</jersey.version>
 		<hibernate.version>5.1.11.Final</hibernate.version>
 	</properties>
@@ -71,8 +72,6 @@
 					<compilerVersion>${java.version}</compilerVersion>
 					<source>${java.version}</source>
 					<target>${java.version}</target>
-					<verbose>true</verbose>
-          			<fork>true</fork>
 					<!-- <compilerArguments>
 						<processor>lombok.launch.AnnotationProcessorHider$AnnotationProcessor</processor>
 						<processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
@@ -107,7 +106,7 @@
 			</plugin>
 			<plugin>
 				<artifactId>maven-shade-plugin</artifactId>
-				<version>2.1</version>
+				<version>3.1.0</version>
 				<executions>
 					<!-- option 1 -->
 					<execution>
@@ -204,7 +203,7 @@
 		<dependency>
 			<groupId>mysql</groupId>
 			<artifactId>mysql-connector-java</artifactId>
-			<version>5.1.21</version>
+			<version>6.0.6</version>
 		</dependency>
 
 		<!-- Jersey -->
@@ -235,12 +234,12 @@
 		<dependency>
 		    <groupId>org.springframework.security</groupId>
 		    <artifactId>spring-security-core</artifactId>
-		    <version>4.2.3.RELEASE</version>
+		    <version>${spring-security.version}</version>
 		</dependency>
 		<dependency>
 		    <groupId>org.springframework.security</groupId>
 		    <artifactId>spring-security-web</artifactId>
-		    <version>4.2.3.RELEASE</version>
+		    <version>${spring-security.version}</version>
 		</dependency>
 
 		<!-- Flyway -->
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 8fdf556..7b9c48c 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
@@ -31,6 +31,10 @@
 
     private String authenticationScheme;
 
+    private boolean isSoftDeleteAutoGroup;
+    private boolean isSoftDeleteGroup;
+    private boolean isSoftDeleteGroupMember;
+
     public FullConfiguration (Properties properties) throws IOException {
         super(properties);
     }
@@ -44,23 +48,35 @@
 
         // EM: pattern for matching availability in Krill matches
         setLicensePatterns(properties);
-
+        setDeleteConfiguration(properties);
         ldapConfig = properties.getProperty("ldap.config");
     }
 
+    private void setDeleteConfiguration (Properties properties) {
+        setSoftDeleteGroup(parseDeleteConfig(properties.getProperty("delete.group", "")));
+        setSoftDeleteAutoGroup(parseDeleteConfig(properties.getProperty("delete.auto.group", "")));
+        setSoftDeleteGroupMember(parseDeleteConfig(
+                properties.getProperty("delete.group.member", "")));
+    }
+
+    private boolean parseDeleteConfig (String deleteConfig) {
+        return deleteConfig.equals("soft") ? true : false;
+    }
+
     private void setLicensePatterns (Properties properties) {
         setFreeLicensePattern(compilePattern(getFreeOnlyRegex()));
-        setPublicLicensePattern(
-                compilePattern(getFreeOnlyRegex() + "|" + getPublicOnlyRegex()));
-        setAllLicensePattern(compilePattern(
-                getFreeOnlyRegex() + "|" + getPublicOnlyRegex() + "|" + getAllOnlyRegex()));
+        setPublicLicensePattern(compilePattern(
+                getFreeOnlyRegex() + "|" + getPublicOnlyRegex()));
+        setAllLicensePattern(compilePattern(getFreeOnlyRegex() + "|"
+                + getPublicOnlyRegex() + "|" + getAllOnlyRegex()));
     }
 
     private void setLicenseRegex (Properties properties) {
         setFreeOnlyRegex(properties.getProperty("availability.regex.free", ""));
         freeRegexList = splitAndAddToList(getFreeOnlyRegex());
 
-        setPublicOnlyRegex(properties.getProperty("availability.regex.public", ""));
+        setPublicOnlyRegex(
+                properties.getProperty("availability.regex.public", ""));
         publicRegexList = splitAndAddToList(getPublicOnlyRegex());
 
         setAllOnlyRegex(properties.getProperty("availability.regex.all", ""));
@@ -76,7 +92,7 @@
                 list.add(s.trim());
             }
         }
-        else{
+        else {
             list = new ArrayList<>(1);
             list.add(regex);
         }
@@ -177,4 +193,28 @@
         this.allOnlyRegex = allOnlyRegex;
     }
 
+    public boolean isSoftDeleteGroup () {
+        return isSoftDeleteGroup;
+    }
+
+    public void setSoftDeleteGroup (boolean isSoftDeleteGroup) {
+        this.isSoftDeleteGroup = isSoftDeleteGroup;
+    }
+
+    public boolean isSoftDeleteGroupMember () {
+        return isSoftDeleteGroupMember;
+    }
+
+    public void setSoftDeleteGroupMember (boolean isSoftDeleteGroupMember) {
+        this.isSoftDeleteGroupMember = isSoftDeleteGroupMember;
+    }
+
+    public boolean isSoftDeleteAutoGroup () {
+        return isSoftDeleteAutoGroup;
+    }
+
+    public void setSoftDeleteAutoGroup (boolean isSoftDeleteAutoGroup) {
+        this.isSoftDeleteAutoGroup = isSoftDeleteAutoGroup;
+    }
+
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupMemberDao.java b/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupMemberDao.java
index aeb413e..9803461 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupMemberDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupMemberDao.java
@@ -60,12 +60,18 @@
         entityManager.persist(member);
     }
 
-    public void deleteMember (String userId, int groupId, String deletedBy, boolean isSoftDelete)
-            throws KustvaktException {
+    public void deleteMember (String userId, int groupId, String deletedBy,
+            boolean isSoftDelete) throws KustvaktException {
         ParameterChecker.checkStringValue(userId, "userId");
         ParameterChecker.checkIntegerValue(groupId, "groupId");
 
         UserGroupMember member = retrieveMemberById(userId, groupId);
+        GroupMemberStatus status = member.getStatus();
+        if (isSoftDelete && status.equals(GroupMemberStatus.DELETED)) {
+            throw new KustvaktException(StatusCodes.DB_ENTRY_DELETED,
+                    userId + " has already been deleted from the group.",
+                    userId);
+        }
 
         if (isSoftDelete) {
             member.setStatus(GroupMemberStatus.DELETED);
diff --git a/full/src/main/java/de/ids_mannheim/korap/handlers/EntityDao.java b/full/src/main/java/de/ids_mannheim/korap/handlers/EntityDao.java
index 3d75697..8577a1b 100644
--- a/full/src/main/java/de/ids_mannheim/korap/handlers/EntityDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/handlers/EntityDao.java
@@ -266,12 +266,12 @@
             jlog.error("Could not create user account with username: {}",
                     user.getUsername());
             throw new DatabaseException(e, user.getUsername(), "korap_users",
-                    StatusCodes.ENTRY_EXISTS, "Username exists.",
+                    StatusCodes.DB_ENTRY_EXISTS, "Username exists.",
                     user.getUsername());
         }
         catch (DataAccessException e) {
             throw new DatabaseException(e, user.getUsername(), "korap_users",
-                    StatusCodes.ENTRY_EXISTS, "Username exists.",
+                    StatusCodes.DB_ENTRY_EXISTS, "Username exists.",
                     user.getUsername());
         }
 
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 02cff45..15f6052 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
@@ -7,6 +7,7 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import de.ids_mannheim.korap.config.FullConfiguration;
 import de.ids_mannheim.korap.constant.GroupMemberStatus;
 import de.ids_mannheim.korap.constant.PredefinedRole;
 import de.ids_mannheim.korap.constant.UserGroupStatus;
@@ -46,6 +47,8 @@
     private UserGroupConverter converter;
     @Autowired
     private AuthenticationManagerIface authManager;
+    @Autowired
+    private FullConfiguration config;
 
     private static List<Role> memberRoles;
 
@@ -91,7 +94,7 @@
                 break;
             }
         }
-        
+
         return members;
     }
 
@@ -175,7 +178,13 @@
         User user = authManager.getUser(username);
         UserGroup userGroup = userGroupDao.retrieveGroupById(groupId);
         if (userGroup.getCreatedBy().equals(username) || user.isSystemAdmin()) {
-            userGroupDao.deleteGroup(groupId, username, false);
+            // soft delete
+            userGroupDao.deleteGroup(groupId, username,
+                    config.isSoftDeleteGroup());
+        }
+        else {
+            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+                    "Unauthorized operation for user: " + username, username);
         }
     }
 
@@ -190,7 +199,7 @@
     public void deleteAutoHiddenGroup (int groupId, String deletedBy)
             throws KustvaktException {
         // default hard delete
-        userGroupDao.deleteGroup(groupId, deletedBy, false);
+        userGroupDao.deleteGroup(groupId, deletedBy, config.isSoftDeleteAutoGroup());
     }
 
     /** Adds a user to the specified usergroup. If the username with 
@@ -216,9 +225,10 @@
         ParameterChecker.checkIntegerValue(groupId, "userGroupId");
 
         if (memberExists(username, groupId, status)) {
-            throw new KustvaktException(StatusCodes.ENTRY_EXISTS,
-                    "Username: " + username + " with status " + status
-                            + " exists in usergroup " + userGroup.getName());
+            throw new KustvaktException(StatusCodes.DB_ENTRY_EXISTS,
+                    "Username " + username + " with status " + status
+                            + " exists in user-group " + userGroup.getName(),
+                    username, status.name(), userGroup.getName());
         }
 
         setMemberRoles();
@@ -250,7 +260,7 @@
             return true;
         }
         else if (existingStatus.equals(GroupMemberStatus.DELETED)) {
-            // hard delete
+            // hard delete, not customizable
             groupMemberDao.deleteMember(username, groupId, "system", false);
         }
 
@@ -312,7 +322,8 @@
      */
     public void unsubscribe (int groupId, String username)
             throws KustvaktException {
-        groupMemberDao.deleteMember(username, groupId, username, true);
+        groupMemberDao.deleteMember(username, groupId, username,
+                config.isSoftDeleteGroupMember());
     }
 
 
@@ -340,7 +351,9 @@
         }
         else if (isUserGroupAdmin(deletedBy, userGroup)
                 || user.isSystemAdmin()) {
-            groupMemberDao.deleteMember(memberId, groupId, deletedBy, false);
+            // soft delete
+            groupMemberDao.deleteMember(memberId, groupId, deletedBy,
+                    config.isSoftDeleteGroupMember());
         }
         else {
             throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
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 2ddec6f..ad92c84 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
@@ -111,6 +111,12 @@
         }
     }
 
+    /** Only group owner and system admins can delete groups. 
+     * 
+     * @param securityContext
+     * @param groupId
+     * @return HTTP 200, if successful.
+     */
     @DELETE
     @Path("delete")
     @Consumes(MediaType.APPLICATION_JSON)
diff --git a/full/src/main/resources/kustvakt.conf b/full/src/main/resources/kustvakt.conf
index ee3bb7d..d8ff39d 100644
--- a/full/src/main/resources/kustvakt.conf
+++ b/full/src/main/resources/kustvakt.conf
@@ -17,6 +17,11 @@
 default.layer.d = mate
 default.layer.c = corenlp
 
+## delete configuration (default hard)
+# delete.auto.group = hard
+delete.group = soft
+delete.group.member = soft
+
 ## availability regex
 ## only support |
 availability.regex.free = CC-BY.*
diff --git a/full/src/test/java/de/ids_mannheim/korap/dao/UserGroupDaoTest.java b/full/src/test/java/de/ids_mannheim/korap/dao/UserGroupDaoTest.java
index d9fdca4..d40ee83 100644
--- a/full/src/test/java/de/ids_mannheim/korap/dao/UserGroupDaoTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/dao/UserGroupDaoTest.java
@@ -13,6 +13,7 @@
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 
+import de.ids_mannheim.korap.config.FullConfiguration;
 import de.ids_mannheim.korap.constant.GroupMemberStatus;
 import de.ids_mannheim.korap.constant.PredefinedRole;
 import de.ids_mannheim.korap.constant.UserGroupStatus;
@@ -35,10 +36,12 @@
     private VirtualCorpusDao virtualCorpusDao;
     @Autowired
     private RoleDao roleDao;
+    @Autowired
+    private FullConfiguration config;
 
     @Rule
     public ExpectedException thrown = ExpectedException.none();
-
+    
 
     @Test
     public void createDeleteNewUserGroup () throws KustvaktException {
@@ -74,7 +77,7 @@
         assertEquals(0, vc.size());
 
         // soft delete group
-        userGroupDao.deleteGroup(groupId, createdBy, true);
+        userGroupDao.deleteGroup(groupId, createdBy, config.isSoftDeleteGroup());
         group = userGroupDao.retrieveGroupById(groupId);
         assertEquals(UserGroupStatus.DELETED, group.getStatus());
 
diff --git a/full/src/test/java/de/ids_mannheim/korap/misc/KoralNodeTest.java b/full/src/test/java/de/ids_mannheim/korap/misc/KoralNodeTest.java
index 4e097ed..c0c7012 100644
--- a/full/src/test/java/de/ids_mannheim/korap/misc/KoralNodeTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/misc/KoralNodeTest.java
@@ -1,8 +1,12 @@
 package de.ids_mannheim.korap.misc;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
 import com.fasterxml.jackson.databind.node.ObjectNode;
+
 import de.ids_mannheim.korap.resource.rewrite.KoralNode;
 import de.ids_mannheim.korap.utils.JsonUtils;
-import org.junit.Test;
 
 /**
  * @author hanl
@@ -18,7 +22,7 @@
         KoralNode knode = KoralNode.wrapNode(node);
         knode.put("value_1", "setting_1");
 
-        System.out.println(knode.rawNode().toString());
+        assertEquals("{\"value_1\":\"setting_1\"}",knode.rawNode().toString());
     }
 
 
@@ -28,7 +32,7 @@
         node.put("value_1", "setting_1");
         KoralNode knode = KoralNode.wrapNode(node);
         knode.remove("value_1", null);
-        System.out.println(knode.rawNode().toString());
+        assertEquals("{}",knode.rawNode().toString());
     }
 
 
@@ -38,7 +42,7 @@
         node.put("value_1", "setting_1");
         KoralNode knode = KoralNode.wrapNode(node);
         knode.replace("value_1", "settings_2", null);
-        System.out.println(knode.rawNode().toString());
+        assertEquals("{\"value_1\":\"settings_2\"}",knode.rawNode().toString());
     }
 
 
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 edafea8..537f299 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
@@ -32,6 +32,22 @@
     private HttpAuthorizationHandler handler;
     private String username = "UserGroupControllerTest";
 
+    private JsonNode retrieveUserGroups (String username)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        ClientResponse response = resource().path("group").path("list")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(username,
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .get(ClientResponse.class);
+        String entity = response.getEntity(String.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        return JsonUtils.readTree(entity);
+    }
+
     // dory is a group admin in dory group
     @Test
     public void testListDoryGroups () throws KustvaktException {
@@ -158,35 +174,16 @@
         assertEquals(PredefinedRole.VC_ACCESS_MEMBER.name(),
                 node.at("/members/1/roles/1").asText());
 
-        testDeleteGroupMemberUnauthorized(groupId);
-        testDeleteGroupMember(groupId);
+
+        testInviteMember(groupId);
+
+        testDeleteMemberUnauthorized(groupId);
+        testDeleteMember(groupId);
         testDeleteGroup(groupId);
     }
 
-    private void testDeleteGroupMemberUnauthorized (String groupId)
-            throws UniformInterfaceException, ClientHandlerException,
-            KustvaktException {
-        // nemo is a group member
-        ClientResponse response = resource().path("group").path("member")
-                .path("delete").queryParam("memberId", "marlin")
-                .queryParam("groupId", groupId)
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("nemo",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .delete(ClientResponse.class);
 
-        String entity = response.getEntity(String.class);
-//        System.out.println(entity);
-        JsonNode node = JsonUtils.readTree(entity);
-        assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
-        assertEquals(StatusCodes.AUTHORIZATION_FAILED,
-                node.at("/errors/0/0").asInt());
-        assertEquals("Unauthorized operation for user: nemo",
-                node.at("/errors/0/1").asText());
-    }
-
-    private void testDeleteGroupMember (String groupId)
+    private void testDeleteMember (String groupId)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
         // delete marlin from group
@@ -209,13 +206,80 @@
         String entity = response.getEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
         node = node.get(0);
-        assertEquals(2, node.get("members").size());
+        assertEquals(3, node.get("members").size());
         assertEquals("nemo", node.at("/members/1/userId").asText());
         assertEquals(GroupMemberStatus.PENDING.name(),
                 node.at("/members/1/status").asText());
 
     }
 
+    private void testDeleteMemberUnauthorized (String groupId)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        // nemo is a group member
+        ClientResponse response = resource().path("group").path("member")
+                .path("delete").queryParam("memberId", "marlin")
+                .queryParam("groupId", groupId)
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue("nemo",
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .delete(ClientResponse.class);
+
+        String entity = response.getEntity(String.class);
+        //        System.out.println(entity);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
+        assertEquals(StatusCodes.AUTHORIZATION_FAILED,
+                node.at("/errors/0/0").asInt());
+        assertEquals("Unauthorized operation for user: nemo",
+                node.at("/errors/0/1").asText());
+    }
+
+    private void testDeletePendingMember () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        // dory delete pearl
+        ClientResponse response = resource().path("group").path("member")
+                .path("delete").queryParam("memberId", "pearl")
+                // dory group
+                .queryParam("groupId", "2")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue("dory",
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .delete(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        // check member
+        JsonNode node = retrieveUserGroups("pearl");
+        assertEquals(0, node.size());
+    }
+
+    @Test
+    public void testDeleteDeletedMember () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        ClientResponse response = resource().path("group").path("member")
+                .path("delete").queryParam("memberId", "pearl")
+                // dory group
+                .queryParam("groupId", "2")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue("dory",
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .delete(ClientResponse.class);
+
+        String entity = response.getEntity(String.class);
+        System.out.println(entity);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+        assertEquals(StatusCodes.DB_ENTRY_DELETED,
+                node.at("/errors/0/0").asInt());
+        assertEquals("pearl has already been deleted from the group.",
+                node.at("/errors/0/1").asText());
+        assertEquals("pearl", node.at("/errors/0/2").asText());
+    }
+
     private void testDeleteGroup (String groupId)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
@@ -242,10 +306,32 @@
     }
 
     @Test
+    public void testDeleteGroupUnauthorized () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        // dory is a group admin in marlin group
+        ClientResponse response = resource().path("group").path("delete")
+                .queryParam("groupId", "1")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue("dory",
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .delete(ClientResponse.class);
+
+        String entity = response.getEntity(String.class);
+        //        System.out.println(entity);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
+        assertEquals(StatusCodes.AUTHORIZATION_FAILED,
+                node.at("/errors/0/0").asInt());
+        assertEquals("Unauthorized operation for user: dory",
+                node.at("/errors/0/1").asText());
+    }
+
+    @Test
     public void testDeleteGroupOwner () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
         // delete marlin from marlin group
-        // dory is a VCA in marlin group
+        // dory is a group admin in marlin group
         ClientResponse response = resource().path("group").path("member")
                 .path("delete").queryParam("memberId", "marlin")
                 .queryParam("groupId", "1")
@@ -262,95 +348,51 @@
         assertEquals(StatusCodes.NOT_ALLOWED, node.at("/errors/0/0").asInt());
         assertEquals("Operation 'delete group owner'is not allowed.",
                 node.at("/errors/0/1").asText());
-
     }
 
-    //    @Test
-    //    public void testInviteMember () {
-    //
-    //    }
-    //
-    //    @Test
-    //    public void testInviteDeletedMember () {
-    //
-    //    }
-    //    
-    //    @Test
-    //    public void testDeletePendingMember () {
-    //
-    //    }
+    private void testInviteMember (String groupId)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        String[] members = new String[] { "darla" };
 
+        UserGroupJson userGroup = new UserGroupJson();
+        userGroup.setMembers(members);
+        userGroup.setId(Integer.parseInt(groupId));
 
-    // marlin has GroupMemberStatus.PENDING in dory group
-    @Test
-    public void testSubscribeMarlinToDoryGroup () throws KustvaktException {
-        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
-        form.add("groupId", "2");
-
-        ClientResponse response = resource().path("group").path("subscribe")
-                .type(MediaType.APPLICATION_FORM_URLENCODED)
+        ClientResponse response = resource().path("group").path("member")
+                .path("invite").type(MediaType.APPLICATION_JSON)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("marlin",
+                        handler.createBasicAuthorizationHeaderValue(username,
                                 "pass"))
-                .entity(form).post(ClientResponse.class);
+                .entity(userGroup).post(ClientResponse.class);
 
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
-        // retrieve marlin group
-        JsonNode node = retrieveMarlinGroups();
-        System.out.println(node);
-        assertEquals(2, node.size());
-
-        JsonNode group = node.get(1);
-        assertEquals(2, group.at("/id").asInt());
-        assertEquals("dory group", group.at("/name").asText());
-        assertEquals("dory", group.at("/owner").asText());
-        // group members are not allowed to see other members
-        assertEquals(0, group.at("/members").size());
-        assertEquals(GroupMemberStatus.ACTIVE.name(),
-                group.at("/userMemberStatus").asText());
-
-        // unsubscribe marlin from dory group
-        testUnsubscribe(form);
-
-        // invite marlin to dory group to set back the GroupMemberStatus.PENDING
-        testInviteMember();
-    }
-
-    private JsonNode retrieveMarlinGroups () throws UniformInterfaceException,
-            ClientHandlerException, KustvaktException {
-        ClientResponse response = resource().path("group").path("list")
+        // list group
+        response = resource().path("group").path("list")
                 .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("marlin",
+                        handler.createBasicAuthorizationHeaderValue(username,
                                 "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .get(ClientResponse.class);
+
         String entity = response.getEntity(String.class);
 
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        JsonNode node = JsonUtils.readTree(entity);
+        node = node.get(0);
+        assertEquals(4, node.get("members").size());
 
-        return JsonUtils.readTree(entity);
+        assertEquals("darla", node.at("/members/3/userId").asText());
+        assertEquals(GroupMemberStatus.PENDING.name(),
+                node.at("/members/3/status").asText());
+        assertEquals(PredefinedRole.USER_GROUP_MEMBER.name(),
+                node.at("/members/3/roles/0").asText());
+        assertEquals(PredefinedRole.VC_ACCESS_MEMBER.name(),
+                node.at("/members/3/roles/1").asText());
     }
 
-    private void testUnsubscribe (MultivaluedMap<String, String> form)
-            throws UniformInterfaceException, ClientHandlerException,
-            KustvaktException {
-        ClientResponse response = resource().path("group").path("unsubscribe")
-                .type(MediaType.APPLICATION_FORM_URLENCODED)
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("marlin",
-                                "pass"))
-                .entity(form).post(ClientResponse.class);
-
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-
-        JsonNode node = retrieveMarlinGroups();
-        assertEquals(1, node.size());
-    }
-
-    private void testInviteMember () throws UniformInterfaceException,
+    private void testInviteDeletedMember () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
         String[] members = new String[] { "marlin" };
 
@@ -370,7 +412,7 @@
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
         // check member
-        JsonNode node = retrieveMarlinGroups();
+        JsonNode node = retrieveUserGroups("marlin");
         assertEquals(2, node.size());
         JsonNode group = node.get(1);
         assertEquals(GroupMemberStatus.PENDING.name(),
@@ -378,9 +420,110 @@
 
     }
 
+    @Test
+    public void testInvitePendingMember () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        // marlin has status PENDING in dory group
+        String[] members = new String[] { "marlin" };
+
+        UserGroupJson userGroup = new UserGroupJson();
+        userGroup.setMembers(members);
+        // dory group
+        userGroup.setId(2);
+
+        ClientResponse response = resource().path("group").path("member")
+                .path("invite").type(MediaType.APPLICATION_JSON)
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue("dory",
+                                "pass"))
+                .entity(userGroup).post(ClientResponse.class);
+        String entity = response.getEntity(String.class);
+        //        System.out.println(entity);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+        assertEquals(StatusCodes.DB_ENTRY_EXISTS,
+                node.at("/errors/0/0").asInt());
+        assertEquals("Username marlin with status PENDING exists in user-group "
+                + "dory group", node.at("/errors/0/1").asText());
+        assertEquals("[marlin, PENDING, dory group]",
+                node.at("/errors/0/2").asText());
+    }
+
+    @Test
+    public void testInviteDeletedMember2 () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        // pearl has status deleted in dory group
+        String[] members = new String[] { "pearl" };
+
+        UserGroupJson userGroup = new UserGroupJson();
+        userGroup.setMembers(members);
+        // dory group
+        userGroup.setId(2);
+
+        ClientResponse response = resource().path("group").path("member")
+                .path("invite").type(MediaType.APPLICATION_JSON)
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue("dory",
+                                "pass"))
+                .entity(userGroup).post(ClientResponse.class);
+
+//        String entity = response.getEntity(String.class);
+//        System.out.println(entity);
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        // check member
+        JsonNode node = retrieveUserGroups("pearl");
+        assertEquals(1, node.size());
+        JsonNode group = node.get(0);
+        assertEquals(GroupMemberStatus.PENDING.name(),
+                group.at("/userMemberStatus").asText());
+
+        testDeletePendingMember();
+    }
+
+
+    // marlin has GroupMemberStatus.PENDING in dory group
+    @Test
+    public void testSubscribePendingMember () throws KustvaktException {
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        form.add("groupId", "2");
+
+        ClientResponse response = resource().path("group").path("subscribe")
+                .type(MediaType.APPLICATION_FORM_URLENCODED)
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue("marlin",
+                                "pass"))
+                .entity(form).post(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        // retrieve marlin group
+        JsonNode node = retrieveUserGroups("marlin");
+        //        System.out.println(node);
+        assertEquals(2, node.size());
+
+        JsonNode group = node.get(1);
+        assertEquals(2, group.at("/id").asInt());
+        assertEquals("dory group", group.at("/name").asText());
+        assertEquals("dory", group.at("/owner").asText());
+        // group members are not allowed to see other members
+        assertEquals(0, group.at("/members").size());
+        assertEquals(GroupMemberStatus.ACTIVE.name(),
+                group.at("/userMemberStatus").asText());
+
+        // unsubscribe marlin from dory group
+        testUnsubscribeActiveMember(form);
+
+        // invite marlin to dory group to set back the GroupMemberStatus.PENDING
+        testInviteDeletedMember();
+    }
+
     // pearl has GroupMemberStatus.DELETED in dory group
     @Test
-    public void testSubscribePearlToDoryGroup () throws KustvaktException {
+    public void testSubscribeDeletedMember () throws KustvaktException {
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         form.add("groupId", "2");
 
@@ -464,4 +607,22 @@
                 node.at("/errors/0/1").asText());
     }
 
+    private void testUnsubscribeActiveMember (
+            MultivaluedMap<String, String> form)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        ClientResponse response = resource().path("group").path("unsubscribe")
+                .type(MediaType.APPLICATION_FORM_URLENCODED)
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue("marlin",
+                                "pass"))
+                .entity(form).post(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        JsonNode node = retrieveUserGroups("marlin");
+        assertEquals(1, node.size());
+    }
+
 }
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 6207cb2..0df3720 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
@@ -170,8 +170,8 @@
             ClientHandlerException, KustvaktException {
         ClientResponse response = resource().path("vc").path("search").path("4")
                 .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue(
-                                "gill", "pass"))
+                        handler.createBasicAuthorizationHeaderValue("gill",
+                                "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .get(ClientResponse.class);
         String entity = response.getEntity(String.class);
@@ -274,7 +274,7 @@
                 .get(ClientResponse.class);
         entity = response.getEntity(String.class);
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-//                System.out.println(entity);
+        //                System.out.println(entity);
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(2, node.size());
         assertEquals("new vc", node.get(1).get("name").asText());
@@ -361,9 +361,12 @@
 
         InputStream is = getClass().getClassLoader()
                 .getResourceAsStream("test-user.token");
-        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
 
-        String authToken = reader.readLine();
+        String authToken;
+        try (BufferedReader reader =
+                new BufferedReader(new InputStreamReader(is));) {
+            authToken = reader.readLine();
+        }
 
         ClientResponse response = resource().path("vc").path("create")
                 .header(Attributes.AUTHORIZATION,
@@ -478,14 +481,13 @@
 
     @Test
     public void testDeleteVCUnauthorized () throws KustvaktException {
-        ClientResponse response =
-                resource().path("vc").path("delete").path("1")
-                        .header(Attributes.AUTHORIZATION,
-                                handler.createBasicAuthorizationHeaderValue(
-                                        "VirtualCorpusControllerTest", "pass"))
-                        .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+        ClientResponse response = resource().path("vc").path("delete").path("1")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(
+                                "VirtualCorpusControllerTest", "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
 
-                        .delete(ClientResponse.class);
+                .delete(ClientResponse.class);
 
         String entity = response.getEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
@@ -582,8 +584,8 @@
 
         checkWWWAuthenticateHeader(response);
     }
-    
-    
+
+
     /**
      * @see VirtualCorpusServiceTest
      * @throws KustvaktException
@@ -591,8 +593,7 @@
     @Test
     public void testEditPublishVC () throws KustvaktException {
 
-        String json =
-                "{\"id\": \"2\", \"type\": \"PUBLISHED\"}";
+        String json = "{\"id\": \"2\", \"type\": \"PUBLISHED\"}";
 
         ClientResponse response = resource().path("vc").path("edit")
                 .header(Attributes.AUTHORIZATION,
@@ -619,13 +620,12 @@
         JsonNode n = node.get(1);
         assertEquals(VirtualCorpusType.PUBLISHED.displayName(),
                 n.get("type").asText());
-        
+
         //check VC access
         // need system admin account
-        
+
         // edit 2nd
-        json =
-                "{\"id\": \"2\", \"type\": \"PROJECT\"}";
+        json = "{\"id\": \"2\", \"type\": \"PROJECT\"}";
 
         response = resource().path("vc").path("edit")
                 .header(Attributes.AUTHORIZATION,
@@ -636,7 +636,7 @@
                 .post(ClientResponse.class, json);
 
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        
+
         response = resource().path("vc").path("list").path("user")
                 .header(Attributes.AUTHORIZATION,
                         handler.createBasicAuthorizationHeaderValue("dory",
@@ -663,7 +663,7 @@
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .get(ClientResponse.class);
         String entity = response.getEntity(String.class);
-//                System.out.println(entity);
+        //                System.out.println(entity);
         JsonNode node = JsonUtils.readTree(entity);
         assertEquals(1, node.at("/0/accessId").asInt());
         assertEquals(2, node.at("/0/vcId").asInt());
@@ -720,7 +720,7 @@
         assertEquals("group VC", node.at("/0/vcName").asText());
         assertEquals(2, node.at("/0/userGroupId").asInt());
         assertEquals("dory group", node.at("/0/userGroupName").asText());
-    }   
+    }
 
 
     @Test
@@ -844,11 +844,10 @@
         assertEquals("Unauthorized operation for user: dory",
                 node.at("/errors/0/1").asText());
     }
-    
+
     @Test
-    public void testCreateAccessByNonVCA ()
-            throws UniformInterfaceException, ClientHandlerException,
-            KustvaktException {
+    public void testCreateAccessByNonVCA () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
 
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         // nemo vc
diff --git a/full/src/test/resources/kustvakt-test.conf b/full/src/test/resources/kustvakt-test.conf
index 7ec4381..3d5eadd 100644
--- a/full/src/test/resources/kustvakt-test.conf
+++ b/full/src/test/resources/kustvakt-test.conf
@@ -22,6 +22,11 @@
 default.layer.d = mate
 default.layer.c = corenlp
 
+## delete configuration (default hard)
+# delete.auto.group = hard
+delete.group = soft
+delete.group.member = soft
+
 ## availability regex
 ## only support |
 availability.regex.free = CC-BY.*
diff --git a/lite/Changes b/lite/Changes
index 2444966..4e615e9 100644
--- a/lite/Changes
+++ b/lite/Changes
@@ -1,6 +1,7 @@
-0.59.9 2018-01-17
+0.59.9 2018-02-01
 	- renamed light to lite (margaretha)
 	- added Changes file (margaretha)
+	- updated library versions and java environment (margaretha)
 0.59.8 2018-01-17 
 	- restructured Kustvakt and created /lite project
 	- removed version from service paths (margaretha)
diff --git a/lite/pom.xml b/lite/pom.xml
index 3eb4e15..dc9049c 100644
--- a/lite/pom.xml
+++ b/lite/pom.xml
@@ -5,9 +5,9 @@
 	<artifactId>Kustvakt-lite</artifactId>
 	<version>0.59.9</version>
 	<properties>
-		<java.version>1.7</java.version>
+		<java.version>1.8</java.version>
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-		<spring-framework.version>4.3.11.RELEASE</spring-framework.version>
+		<spring-framework.version>5.0.3.RELEASE</spring-framework.version>
 		<jersey.version>1.19.4</jersey.version>
 	</properties>
 
@@ -66,7 +66,7 @@
 			<plugin>
 				<groupId>org.apache.maven.plugins</groupId>
 				<artifactId>maven-compiler-plugin</artifactId>
-				<version>3.3</version>
+				<version>3.7.0</version>
 				<configuration>
 					<compilerVersion>${java.version}</compilerVersion>
 					<source>${java.version}</source>
@@ -98,7 +98,7 @@
 			</plugin>
 			<plugin>
 				<artifactId>maven-shade-plugin</artifactId>
-				<version>2.1</version>
+				<version>3.1.0</version>
 				<executions>
 					<!-- option 1 -->
 					<execution>