Resolved #42 - Added close index controller.

Change-Id: I0df990b1bf2f7d7f92ac4f0e90aced584aad265e
diff --git a/core/Changes b/core/Changes
index 8ce8948..85f9f42 100644
--- a/core/Changes
+++ b/core/Changes
@@ -1,3 +1,7 @@
+# version 0.62
+18/03/2019
+   - Added close index controller (margaretha)
+
 # version 0.61.6
 06/02/2019
    - Added default foundry for structure layer (margaretha)
diff --git a/core/pom.xml b/core/pom.xml
index d07e1a4..eed745d 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -3,7 +3,7 @@
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>de.ids_mannheim.korap</groupId>
 	<artifactId>Kustvakt-core</artifactId>
-	<version>0.61.6</version>
+	<version>0.62</version>
 
 	<properties>
 		<java.version>1.8</java.version>
diff --git a/core/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java b/core/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java
index 35c99c6..8144124 100644
--- a/core/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java
+++ b/core/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java
@@ -62,8 +62,6 @@
     private int validationEmaillength;
 
     private byte[] sharedSecret;
-    @Deprecated
-    private String adminToken;
     private int longTokenTTL;
     private int tokenTTL;
     private int shortTokenTTL;
@@ -107,6 +105,17 @@
         KrillProperties.setProp(properties);
     }
 
+    public KustvaktConfiguration () {}
+    
+    public void loadBasicProperties (Properties properties) {
+        port = new Integer(properties.getProperty("server.port", "8095"));
+        baseURL = properties.getProperty("kustvakt.base.url", "/api/*");
+        setSecureRandomAlgorithm(properties
+                .getProperty("security.secure.random.algorithm", "SHA1PRNG"));
+        setMessageDigestAlgorithm(
+                properties.getProperty("security.md.algorithm", "MD5"));
+    }
+    
     /**
      * loading of the properties and mapping to parameter variables
      * 
@@ -115,6 +124,8 @@
      * @throws Exception
      */
     protected void load (Properties properties) throws Exception {
+        loadBasicProperties(properties);
+        
         currentVersion = properties.getProperty("current.api.version", "v1.0");
         String supportedVersions =
                 properties.getProperty("supported.api.version", "");
@@ -122,11 +133,10 @@
         this.supportedVersions = new HashSet<>(Arrays.asList(supportedVersions.split(" ")));
         this.supportedVersions.add(currentVersion);
 
-        baseURL = properties.getProperty("kustvakt.base.url", "/api/*");
         maxhits = new Integer(properties.getProperty("maxhits", "50000"));
         returnhits = new Integer(properties.getProperty("returnhits", "50000"));
         indexDir = properties.getProperty("krill.indexDir", "");
-        port = new Integer(properties.getProperty("server.port", "8095"));
+        
         // server options
         serverHost = String
                 .valueOf(properties.getProperty("server.host", "localhost"));
@@ -169,7 +179,6 @@
 
         sharedSecret =
                 properties.getProperty("security.sharedSecret", "").getBytes();
-        adminToken = properties.getProperty("security.adminToken");
 
         longTokenTTL = TimeUtils.convertTimeToSeconds(
                 properties.getProperty("security.longTokenTTL", "100D"));
@@ -181,13 +190,6 @@
         // passcodeSaltField =
         // properties.getProperty("security.passcode.salt",
         // "accountCreation");
-        
-        setSecureRandomAlgorithm(properties
-                .getProperty("security.secure.random.algorithm", "SHA1PRNG"));
-
-        setMessageDigestAlgorithm(
-                properties.getProperty("security.md.algorithm", "MD5"));
-
     }
 
     /**
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 446137c..e7a5195 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
@@ -165,22 +165,23 @@
      * 2000 status and error codes concerning authentication
      * 
      * Response with WWW-Authenticate header will be created 
-     * for all KustvaktExceptions with status codes 2000 or greater  
+     * for all KustvaktExceptions with status codes 2001 or greater  
      *  
      * MH: service level messages and callbacks
      */
 
-    public static final int AUTHENTICATION_FAILED = 2000;
-    public static final int LOGIN_FAILED = 2001;
-    public static final int EXPIRED = 2002;
-    public static final int BAD_CREDENTIALS = 2003;
-    public static final int ACCOUNT_NOT_CONFIRMED = 2004;
-    public static final int ACCOUNT_DEACTIVATED = 2005;
-
-    public static final int INVALID_ACCESS_TOKEN = 2006;
+    public static final int INCORRECT_ADMIN_TOKEN = 2000;
     
+    public static final int AUTHENTICATION_FAILED = 2001;
+    public static final int LOGIN_FAILED = 2002;
+    public static final int EXPIRED = 2003;
+    public static final int BAD_CREDENTIALS = 2004;
+    public static final int ACCOUNT_NOT_CONFIRMED = 2005;
+    public static final int ACCOUNT_DEACTIVATED = 2006;
+
     //    public static final int CLIENT_AUTHORIZATION_FAILED = 2013;
     public static final int AUTHORIZATION_FAILED = 2010;
+    public static final int INVALID_ACCESS_TOKEN = 2011;
 
     // 2020 - 2029 reserviert für LDAP-Fehlercodes - 21.04.17/FB
     public static final int LDAP_BASE_ERRCODE = 2020;
diff --git a/core/src/main/java/de/ids_mannheim/korap/server/KustvaktBaseServer.java b/core/src/main/java/de/ids_mannheim/korap/server/KustvaktBaseServer.java
index c5a8648..8de109b 100644
--- a/core/src/main/java/de/ids_mannheim/korap/server/KustvaktBaseServer.java
+++ b/core/src/main/java/de/ids_mannheim/korap/server/KustvaktBaseServer.java
@@ -4,6 +4,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
+import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.security.NoSuchAlgorithmException;
 
@@ -18,6 +19,7 @@
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.springframework.web.context.ContextLoaderListener;
 
+import com.google.common.io.Files;
 import com.sun.jersey.spi.spring.container.servlet.SpringServlet;
 
 import de.ids_mannheim.korap.config.KustvaktConfiguration;
@@ -71,6 +73,24 @@
             kargs.setPort(config.getPort());
         }
 
+        String adminToken="";
+        File f = new File("adminToken");
+        if (!f.exists()) {
+            RandomCodeGenerator random = new RandomCodeGenerator();
+            adminToken = random.createRandomCode(config);
+            FileOutputStream fos = new FileOutputStream(new File("adminToken"));
+            OutputStreamWriter writer =
+                    new OutputStreamWriter(fos, StandardCharsets.UTF_8.name());
+            writer.append("token=");
+            writer.append(adminToken);
+            writer.flush();
+            writer.close();
+        }
+        else {
+            adminToken = Files.readFirstLine(f, Charset.forName("utf-8"))
+                    .substring(6);
+        }
+
         Server server = new Server();
 
         ServletContextHandler contextHandler =
@@ -81,6 +101,7 @@
 
         ServletContextListener listener = new ContextLoaderListener();
         contextHandler.addEventListener(listener);
+        contextHandler.setInitParameter("adminToken", adminToken);
 
         ServletHolder servletHolder = new ServletHolder(new SpringServlet());
         servletHolder.setInitParameter(
@@ -94,17 +115,7 @@
         connector.setPort(kargs.port);
         connector.setIdleTimeout(60000);
 
-        RandomCodeGenerator random = new RandomCodeGenerator();
-        String shutdownToken = random.createRandomCode(config);
-        ShutdownHandler shutdownHandler = new ShutdownHandler(shutdownToken,true,false);
-
-        FileOutputStream fos = new FileOutputStream(new File("shutdownToken"));
-        OutputStreamWriter writer =
-                new OutputStreamWriter(fos, StandardCharsets.UTF_8.name());
-        writer.append("token=");
-        writer.append(shutdownToken);
-        writer.flush();
-        writer.close();
+        ShutdownHandler shutdownHandler = new ShutdownHandler(adminToken,true,true);
 
         HandlerList handlers = new HandlerList();
         handlers.addHandler(shutdownHandler);
diff --git a/core/src/main/java/de/ids_mannheim/korap/service/SearchService.java b/core/src/main/java/de/ids_mannheim/korap/service/SearchService.java
index 3a30fa2..9d2ef02 100644
--- a/core/src/main/java/de/ids_mannheim/korap/service/SearchService.java
+++ b/core/src/main/java/de/ids_mannheim/korap/service/SearchService.java
@@ -6,6 +6,7 @@
 import java.util.regex.Pattern;
 
 import javax.annotation.PostConstruct;
+import javax.servlet.ServletContext;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.UriBuilder;
@@ -283,4 +284,18 @@
     public String getCollocationBase (String query) throws KustvaktException {
         return graphDBhandler.getResponse("distCollo", "q", query);
     }
+    
+    public void closeIndexReader (String token, ServletContext context)
+            throws KustvaktException {
+
+        if (token != null && !token.isEmpty()
+                && token.equals(context.getInitParameter("adminToken"))) {
+            searchKrill.closeIndexReader();
+        }
+        else {
+            throw new KustvaktException(StatusCodes.INCORRECT_ADMIN_TOKEN,
+                    "Admin token is incorrect");
+        }
+    }
+    
 }
diff --git a/core/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java b/core/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
index 4ed62d9..0e2d966 100644
--- a/core/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
+++ b/core/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
@@ -67,6 +67,15 @@
     public KrillIndex getIndex () {
         return index;
     };
+    
+    public void closeIndexReader() throws KustvaktException{
+        try {
+            index.closeReader();
+        }
+        catch (IOException e) {
+            throw new KustvaktException(500, "Failed closing index reader");
+        }
+    }
 
     /**
      * Search in the Lucene index.
diff --git a/core/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java b/core/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java
index 13a89a1..ef3b16b 100644
--- a/core/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java
+++ b/core/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java
@@ -5,6 +5,8 @@
 import java.util.Locale;
 import java.util.Set;
 
+import javax.servlet.ServletContext;
+import javax.ws.rs.FormParam;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
@@ -52,7 +54,8 @@
     private static final boolean DEBUG = false;
 
     private static Logger jlog = LogManager.getLogger(SearchController.class);
-
+    private @Context ServletContext context;
+    
     @Autowired
     private KustvaktResponseHandler kustvaktResponseHandler;
 
@@ -60,7 +63,20 @@
     private SearchService searchService;
     @Autowired
     private OAuth2ScopeService scopeService;
-
+    
+    @POST
+    @Path("{version}/index/close")
+    public Response closeIndexReader (@FormParam("token") String token){
+        try {
+            searchService.closeIndexReader(token, context);
+        }
+        catch (KustvaktException e) {
+            throw kustvaktResponseHandler.throwit(e);
+        }
+        return Response.ok().build();
+    }
+    
+    
     /**
      * Builds a json query serialization from the given parameters.
      *