content: /*
   * Solo - A small and beautiful blogging system written in Java.
   * Copyright (c) 2010-present, b3log.org
   *
   * Solo is licensed under Mulan PSL v2.
   * You can use this software according to the terms and conditions of the Mulan PSL v2.
   * You may obtain a copy of Mulan PSL v2 at:
   *         http://license.coscl.org.cn/MulanPSL2
   * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
   * See the Mulan PSL v2 for more details.
   */
  package org.b3log.solo.service;
  
  import org.apache.logging.log4j.Level;
  import org.apache.logging.log4j.LogManager;
  import org.apache.logging.log4j.Logger;
  import org.b3log.latke.http.Request;
  import org.b3log.latke.ioc.Inject;
  import org.b3log.latke.service.LangPropsService;
  import org.b3log.latke.service.annotation.Service;
  import org.b3log.latke.util.Requests;
  import org.b3log.solo.repository.ArticleRepository;
  import org.b3log.solo.repository.OptionRepository;
  import org.b3log.solo.util.Solos;
  
  import java.util.Iterator;
  import java.util.Map;
  import java.util.concurrent.ConcurrentHashMap;
  
  /**
   * Statistic management service.
   *
   * @author <a href="http://88250.b3log.org">Liang Ding</a>
   * @version 2.0.1.7, Apr 18, 2020
   * @since 0.5.0
   */
  @Service
  public class StatisticMgmtService {
  
      /**
       * Online visitor cache.
       * <p>
       * &lt;ip, recentTime&gt;
       * </p>
       */
      public static final Map<String, Long> ONLINE_VISITORS = new ConcurrentHashMap<>();
  
      /**
       * Logger.
       */
      private static final Logger LOGGER = LogManager.getLogger(StatisticMgmtService.class);
  
      /**
       * Online visitor expiration in 5 minutes.
       */
      private static final int ONLINE_VISITOR_EXPIRATION = 300000;
  
      /**
       * Cookie expiry of "visited".
       */
      private static final int COOKIE_EXPIRY = 60 * 60 * 24; // 24 hours
  
      /**
       * Option repository.
       */
      @Inject
      private OptionRepository optionRepository;
  
      /**
       * Language service.
       */
      @Inject
      private LangPropsService langPropsService;
  
      /**
       * Article repository.
       */
      @Inject
      private ArticleRepository articleRepository;
  
      /**
       * Removes the expired online visitor.
       */
      public static void removeExpiredOnlineVisitor() {
          final long currentTimeMillis = System.currentTimeMillis();
  
          final Iterator<Map.Entry<String, Long>> iterator = ONLINE_VISITORS.entrySet().iterator();
  
          while (iterator.hasNext()) {
              final Map.Entry<String, Long> onlineVisitor = iterator.next();
  
              if (currentTimeMillis > (onlineVisitor.getValue() + ONLINE_VISITOR_EXPIRATION)) {
                  iterator.remove();
                  LOGGER.log(Level.TRACE, "Removed online visitor[ip={}]", onlineVisitor.getKey());
              }
          }
  
          LOGGER.log(Level.DEBUG, "Current online visitor count [{}]", ONLINE_VISITORS.size());
      }
  
      /**
       * Refreshes online visitor count for the specified request.
       *
       * @param request the specified request
       */
      public void onlineVisitorCount(final Request request) {
          if (Solos.isBot(request)) {
              return;
          }
  
          final String remoteAddr = Requests.getRemoteAddr(request);
          LOGGER.log(Level.DEBUG, "Current request [IP={}]", remoteAddr);
          ONLINE_VISITORS.put(remoteAddr, System.currentTimeMillis());
          LOGGER.log(Level.DEBUG, "Current online visitor count [{}]", ONLINE_VISITORS.size());
      }
  }
  