title: How to identify and solve memory leak and too many open file issue in groovy grails application?
body: <p>I am learning to look at heapdump to solve memory leaks. From the heap dump I can see <strong>217</strong> count for <strong>AuditLogger</strong> and <strong>SessionInterceptor</strong> classes. Does this mean there is a memory leak in these classes? </p>
  
  <p>Particularly on the following lines</p>
  
  <ol>
  <li><p>SessionInterceptor class</p>
  
  <p>a. <code>def requestBody = new ObjectMapper().readValue((request.JSON).toString(), Map.class), result = true</code></p>
  
  <p>b. <code>request.getCookies()?.each { cookie -&gt;</code></p>
  
  <p>c. <code>AuditLogger.getInstance().log(result, [:], null, request.getForwardURI(), requestBody)</code></p></li>
  <li><p>AuditLogger class</p>
  
  <p>a. <code>return paramsStripped.keySet().size() &gt; 0 ? JsonOutput.toJson(paramsStripped) : ""</code></p>
  
  <p>b. <code>params?.each { key,value -&gt;</code></p></li>
  </ol>
  
  <p><a href="https://i.stack.imgur.com/tzmnd.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/tzmnd.png" alt="enter image description here"></a>
  SessionInterceptor Class</p>
  
  <pre><code>package com.abc.test.interceptors
  import com.fasterxml.jackson.databind.ObjectMapper
  import com.abc.test.iidiq.summarize.cache.CacheProvider
  import com.abc.test.common.AuditLogger
  import com.abc.test.common.Constants
  import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED
  
  class SessionInterceptor {
  
      def loginService
      int order = 1
  
      SessionInterceptor() {
          matchAll()
      }
  
      boolean before() {
          def requestBody = new ObjectMapper().readValue((request.JSON).toString(), Map.class), result = true
          if(request.getForwardURI().indexOf(Constants.HEALTH_API) &lt; 0) {
              if (request.getForwardURI().indexOf(Constants.LOGIN_API) &gt; -1) {
                  def orgId, userId, sessionId
                  if (requestBody.keySet().size() == 0) {
                      request.getCookies()?.each { cookie -&gt;
                          if (cookie.getName() == Constants.USER_SESSION) {
                              sessionId = cookie.getValue()
                          }
                      }
                      if (sessionId == null) {
                          response.status = SC_UNAUTHORIZED
                          result = false
                      } else {
                          result = true
                      }
                  } else {
                      result = true
                  }
                  if (sessionId != null) {
                      def userInfo = loginService.getData(sessionId)
                      if (userInfo != null) {
                          userId = userInfo[Constants.USER_ID]
                          orgId = userInfo[Constants.ORG_ID]
                      }
                  } else {
                      userId = requestBody[Constants.USER_ID]
                      orgId = requestBody[Constants.ORG_ID]
                  }
                  if (userId != null &amp;&amp; orgId != null &amp;&amp; userId.length() &gt; 0 &amp;&amp; orgId.length() &gt; 0) {
                      AuditLogger.getInstance().log(result, "${userId}  ${orgId}  ${orgId}  \"${Constants.LOGIN_LOG}\"")
                  }
              } else {
                  def sessionId, csrfToken, userInfo, orgId
                  List csrfTokensStored
                  request.getCookies()?.each { cookie -&gt;
                      if (cookie.getName() == Constants.USER_SESSION) {
                          sessionId = cookie.getValue()
                      }
                      if (cookie.getName() == Constants.CSRF_TOKEN) {
                          csrfToken = cookie.getValue()
                      }
                  }
                  if (sessionId != null &amp;&amp; csrfToken != null) {
                      userInfo = loginService.getData(sessionId)
                      if (userInfo == null) {
                          response.status = SC_UNAUTHORIZED
                          result = false
                          AuditLogger.getInstance().log(result, [:], null, request.getForwardURI(), requestBody)
                      } else {
                          Map tokenMap = CacheProvider.getInstance().getCSRFTokenMap()
                          csrfTokensStored = tokenMap.get(sessionId)
                          if (csrfTokensStored != null &amp;&amp; !csrfTokensStored.contains(csrfToken)) {
                              response.status = SC_UNAUTHORIZED
                              result = false
                              orgId = requestBody[Constants.ORG_ID] != null ? requestBody[Constants.ORG_ID] : userInfo[Constants.ORG_ID]
                              AuditLogger.getInstance().log(result, userInfo, orgId, request.getForwardURI(), requestBody)
                          }
                      }
                  } else {
                      response.status = SC_UNAUTHORIZED
                      result = false
                      AuditLogger.getInstance().log(result, [:], null, request.getForwardURI(), requestBody)
                  }
                  if (result) {
                      userInfo = loginService.getData(sessionId)
                      orgId = requestBody[Constants.ORG_ID] != null ? requestBody[Constants.ORG_ID] : userInfo[Constants.ORG_ID]
                      AuditLogger.getInstance().log(result, userInfo, orgId, request.getForwardURI(), requestBody)
                  }
              }
          }
          return result
      }
  }
  </code></pre>
  
  <p>Audit Logger Class</p>
  
  <pre><code>package com.abc.test.common
  
  import groovy.json.JsonOutput
  import org.slf4j.Logger
  import org.slf4j.LoggerFactory
  
  @Singleton
  class AuditLogger {
  
      private Logger logger
  
      private static final screenMap = [
              "/abc/report/dashboard/general": [(Constants.MODULE): "Dashboard", (Constants.SUB_MODULE): "General"],
              "/abc/report/dashboard/analytics": [(Constants.MODULE): "Dashboard", (Constants.SUB_MODULE): "Analytics"],
              "/abc/report/operation/odata": [(Constants.MODULE): "Operation Report", (Constants.SUB_MODULE): "KPI Data"],
              "/abc/report/operation/agenttaskhistory": [(Constants.MODULE): "Operation Report", (Constants.SUB_MODULE): "Task History"],
              "/abc/report/adoption/taskrun": [(Constants.MODULE): "Adoption Report"],
              "/abc/report/bestpractices/user": [(Constants.MODULE): "Bestpractices Report", (Constants.SUB_MODULE): "User Management"],
              "/abc/report/benchmarking/datavolume": [(Constants.MODULE): "Benchmarking Report", (Constants.SUB_MODULE): "Data Volume Analysis"],
              "/abc/report/dashboard/general/preferences": [(Constants.MODULE): "Dashboard", (Constants.SUB_MODULE): "Preferences"],
              "/abc/logout": [(Constants.MODULE): "Logout"],
              "/abc/overview/": [(Constants.MODULE): "Overview", (Constants.SUB_MODULE): "Overview"],
              "/abc/overview/events": [(Constants.MODULE): "Overview", (Constants.SUB_MODULE): "Events Log Table"],
              "/abc/overview/raw": [(Constants.MODULE): "Overview", (Constants.SUB_MODULE): "Raw Events"],
              "/abc/overview/filter": [(Constants.MODULE): "Overview", (Constants.SUB_MODULE): "Apply Filter"],
              "/abc/indexer/logs/upload": [(Constants.MODULE): "Indexer", (Constants.SUB_MODULE): "Upload"],
              "/abc/indexer/logs/download": [(Constants.MODULE): "Indexer", (Constants.SUB_MODULE): "Download"],
              "/abc/report/dashboard/general/timezones": [(Constants.MODULE): "General", (Constants.SUB_MODULE): "Timezones"],
              "/abc/report/dashboard/download": [(Constants.MODULE): "General", (Constants.SUB_MODULE): "Download"]
      ]
  
      void init(){
          logger = LoggerFactory.getLogger(AuditLogger.class.getCanonicalName())
      }
  
      void log(boolean status, String msg){
          this.logMessage(status, msg)
      }
  
      void log(boolean status, def userInfo, def orgId, def url, def requestBody){
          this.logMessage(status, userInfo[Constants.USER_ID] + "  " + userInfo[Constants.ORG_ID] + "  ${orgId}" + this.getModuleNames(url, requestBody) + "  " + this.getParamsString(url, requestBody))
      }
  
      private def logMessage(boolean status, String msg) {
          if(status){
              logger.debug(msg)
          }
          else {
              logger.error(msg)
          }
      }
  
      private def getParamsString(def url, def params){
          if(url == Constants.REPORT_DOWNLOAD) {
              params = params[Constants.FILTERS] != null ? params[Constants.FILTERS] : [:]
          }
          def paramsStripped = [:]
          params?.each { key,value -&gt;
              if(![Constants.USER_ID, Constants.ORG_ID, Constants.SESSION_ID, Constants.QUERY_TYPE].contains(key)){
                  paramsStripped[key] = value
              }
          }
          return paramsStripped.keySet().size() &gt; 0 ? JsonOutput.toJson(paramsStripped) : ""
      }
  
      private def getModuleNames(def url, def requestBody){
          def config = screenMap[url], stringBuilder = new StringBuilder("")
          if(config != null) {
              stringBuilder.append("  \"" + config[Constants.MODULE] + "\"")
              if(url == Constants.REPORT_DOWNLOAD){
                  stringBuilder.append("  \"" + Constants.ROUTE_MAP[requestBody[Constants.ROUTE]] + " " + config[Constants.SUB_MODULE] + "\"")
              }
              else {
                  if(config[Constants.SUB_MODULE] != null){
                      stringBuilder.append("  \"" + config[Constants.SUB_MODULE] + "\"")
                  }
                  else if(config[Constants.MODULE].indexOf(Constants.ADOPTION) &gt; -1){
                      if(requestBody[Constants.QUERY_TYPE] == Constants.UI_SECURE_AGENTS){
                          stringBuilder.append("  \"" + Constants.SECURE_AGENTS_LABEL + "\"")
                      }
                      else if(requestBody[Constants.QUERY_TYPE] == Constants.UI_CONN_TYPE){
                          stringBuilder.append("  \"" + Constants.CONN_TYPE_LABEL + "\"")
                      }
                      else if(requestBody[Constants.QUERY_TYPE] == Constants.UI_APP_TYPE){
                          stringBuilder.append("  \"" + Constants.APP_TYPE_LABEL + "\"")
                      } else {
                          logger.error("Unknown query type: " + requestBody[Constants.QUERY_TYPE])
                      }
                  } else {
                      logger.error("Unknown Module: " + config[Constants.MODULE])
                  }
              }
          } else {
              logger.error("Config: " + config.toString() + ". Url: " + url.toString())
          }
          return stringBuilder.toString()
      }
  }
  </code></pre>
  
  <p><strong>Update</strong></p>
  
  <p>In lot of places I am generating sql queries and urls by replacing placeholders in strings using map and <code>GStringTemplateEngine().createTemplate(source).make(binding).toString()</code> in ConfigProp class. Will this create any File descriptor issue?</p>
  
  <pre><code>//fetch suborgs for parent org
  private def getSuborgList(def sessionId, def userOrgInfo) {        
      def applicationConfig = ConfigProp.getInstance().getApplicationConfig()
      //Here ist params for getTemplateAsString are 
      //1. "https://xxx/api/Orgs('\$org_id')/Suborgs
      //2. ["org_id": "xxx"]
      def suborgFetchUrl = ConfigProp.getInstance().getTemplateAsString(this.getFullUrl(applicationConfig.iics.pod_environments.development.ids[Constants.SUBORGS][Constants.URL]), [(Constants.ORG_ID): userOrgInfo[Constants.ORG_ID]])
      def request = new HttpGet(suborgFetchUrl), suborgs = [userOrgInfo]
      if(userOrgInfo.keySet().size() &gt; 0) {
          request.addHeader(Constants.SESSION_ID_HEADER, sessionId)
          def resp = HttpClientBuilder.create().build().execute(request)
          if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
              def bufferedReader = new BufferedReader(new InputStreamReader(resp.getEntity().getContent()))
              def suborgsRawList = mapper.parseText(bufferedReader.getText())
              suborgs = suborgs + suborgsRawList[Constants.VALUE].collect { org -&gt;
                  return [(Constants.ORG_ID): org[Constants.ID], (Constants.ORG_NAME): org[Constants.NAME]]
              }
          }
      }
      return suborgs
  }
  </code></pre>
  
  <p>ConfigProp class</p>
  
  <pre><code>package com.com.abc.test.queryservice.common
  
  import groovy.text.GStringTemplateEngine
  import groovy.transform.CompileStatic
  import org.slf4j.Logger
  import org.slf4j.LoggerFactory
  
  @Singleton
  class ConfigProp {
      private def esRequsetMappingConfigSlurper
      private def applicationConfigSlurper
  
      private String filePath = "es_request_mapping.groovy"
      private String applicationConfigPath = "application_config.groovy"
  
      private Logger logger = LoggerFactory.getLogger("com.com.abc.test.queryservice.common.ConfigProp")
  
      void init() {
          try {
              esRequsetMappingConfigSlurper = new ConfigSlurper().parse(new File(filePath).toURI().toURL())
              applicationConfigSlurper = new ConfigSlurper().parse(new File(applicationConfigPath).toURI().toURL())
          } catch (FileNotFoundException fnfe) {
              logger.error(fnfe)
          } catch (IOException ioe) {
              logger.error(ioe)
          } catch (Exception e) {
              logger.error(e)
          }
      }
  
      /**
       * Reload configurations
       */
      void reloadEsRequestMappingConfig() {
          init()
          logger.info("Configuration reloaded successfully.")
      }
  
      /**
       * Given a filepath it reads string and returns
       * @return file content as string
       */
      def getTemplateAsString(def source, def binding) {
          try {
              return new GStringTemplateEngine().createTemplate(source).make(binding).toString()
              //return new GStringTemplateEngine().createTemplate(new File(relativePath)).make(binding).toString()
          } catch (FileNotFoundException fnfe) {
              logger.error(fnfe)
          } catch (IOException ioe) {
              logger.error(ioe)
          } catch (Exception e) {
              logger.error(e)
          }
          return null
      }
  
      /**
       * Get ConfigSlurper object for es_request_mapping.groovy configuration file
       * @return Config object
       */
      def getEsRequestMappingConfig() {
          return esRequsetMappingConfigSlurper
      }
      def getApplicationConfig() {
          return applicationConfigSlurper
      }
  
  
      static main(args) {
          ConfigProp.getInstance().init()
          def config = ConfigProp.getInstance().getEsRequestMappingConfig()
      }
  }
  </code></pre>