content: /*
   * Copyright 1999-2018 Alibaba Group Holding Ltd.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package com.alibaba.druid.support.http.stat;
  
  import com.alibaba.druid.support.logging.Log;
  import com.alibaba.druid.support.logging.LogFactory;
  
  import java.util.Map;
  import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
  import java.util.concurrent.atomic.AtomicLongFieldUpdater;
  
  import static com.alibaba.druid.util.JdbcSqlStatUtils.get;
  
  public class WebSessionStat {
      private static final Log LOG = LogFactory.getLog(WebSessionStat.class);
  
      private final String sessionId;
  
      private volatile int runningCount;
      private volatile int concurrentMax;
      static final AtomicIntegerFieldUpdater<WebSessionStat> runningCountUpdater = AtomicIntegerFieldUpdater.newUpdater(WebSessionStat.class,
              "runningCount");
      static final AtomicIntegerFieldUpdater<WebSessionStat> concurrentMaxUpdater = AtomicIntegerFieldUpdater.newUpdater(WebSessionStat.class,
              "concurrentMax");
  
      private volatile long requestCount;
      private volatile long requestErrorCount;
      private volatile long requestTimeNano;
  
      static final AtomicLongFieldUpdater<WebSessionStat> requestCountUpdater = AtomicLongFieldUpdater.newUpdater(WebSessionStat.class,
              "requestCount");
      static final AtomicLongFieldUpdater<WebSessionStat> requestErrorCountUpdater = AtomicLongFieldUpdater.newUpdater(WebSessionStat.class,
              "requestErrorCount");
      static final AtomicLongFieldUpdater<WebSessionStat> requestTimeNanoUpdater = AtomicLongFieldUpdater.newUpdater(WebSessionStat.class,
              "requestTimeNano");
  
      private volatile long jdbcFetchRowCount;
      private volatile long jdbcUpdateCount;
      private volatile long jdbcExecuteCount;
      private volatile long jdbcExecuteTimeNano;
      static final AtomicLongFieldUpdater<WebSessionStat> jdbcFetchRowCountUpdater = AtomicLongFieldUpdater.newUpdater(WebSessionStat.class,
              "jdbcFetchRowCount");
      static final AtomicLongFieldUpdater<WebSessionStat> jdbcUpdateCountUpdater = AtomicLongFieldUpdater.newUpdater(WebSessionStat.class,
              "jdbcUpdateCount");
      static final AtomicLongFieldUpdater<WebSessionStat> jdbcExecuteCountUpdater = AtomicLongFieldUpdater.newUpdater(WebSessionStat.class,
              "jdbcExecuteCount");
      static final AtomicLongFieldUpdater<WebSessionStat> jdbcExecuteTimeNanoUpdater = AtomicLongFieldUpdater.newUpdater(WebSessionStat.class,
              "jdbcExecuteTimeNano");
  
      private volatile long jdbcCommitCount;
      private volatile long jdbcRollbackCount;
      static final AtomicLongFieldUpdater<WebSessionStat> jdbcCommitCountUpdater = AtomicLongFieldUpdater.newUpdater(WebSessionStat.class,
              "jdbcCommitCount");
      static final AtomicLongFieldUpdater<WebSessionStat> jdbcRollbackCountUpdater = AtomicLongFieldUpdater.newUpdater(WebSessionStat.class,
              "jdbcRollbackCount");
  
      private long createTimeMillis = -1L;
      private volatile long lastAccessTimeMillis = -1L;
  
      private String remoteAddresses;
  
      private String principal;
  
      private String userAgent;
  
      private volatile int requestIntervalHistogram_0_1;
      private volatile int requestIntervalHistogram_1_10;
      private volatile int requestIntervalHistogram_10_100;
      private volatile int requestIntervalHistogram_100_1000;
      private volatile int requestIntervalHistogram_1000_10000;
      private volatile int requestIntervalHistogram_10000_100000;
      private volatile int requestIntervalHistogram_100000_1000000;
      private volatile int requestIntervalHistogram_1000000_10000000;
      private volatile int requestIntervalHistogram_10000000_more;
  
      static final AtomicIntegerFieldUpdater<WebSessionStat> requestIntervalHistogram_0_1_Updater = AtomicIntegerFieldUpdater.newUpdater(WebSessionStat.class,
              "requestIntervalHistogram_0_1");
      static final AtomicIntegerFieldUpdater<WebSessionStat> requestIntervalHistogram_1_10_Updater = AtomicIntegerFieldUpdater.newUpdater(WebSessionStat.class,
              "requestIntervalHistogram_1_10");
      static final AtomicIntegerFieldUpdater<WebSessionStat> requestIntervalHistogram_10_100_Updater = AtomicIntegerFieldUpdater.newUpdater(WebSessionStat.class,
              "requestIntervalHistogram_10_100");
      static final AtomicIntegerFieldUpdater<WebSessionStat> requestIntervalHistogram_100_1000_Updater = AtomicIntegerFieldUpdater.newUpdater(WebSessionStat.class,
              "requestIntervalHistogram_100_1000");
      static final AtomicIntegerFieldUpdater<WebSessionStat> requestIntervalHistogram_1000_10000_Updater = AtomicIntegerFieldUpdater.newUpdater(WebSessionStat.class,
              "requestIntervalHistogram_1000_10000");
      static final AtomicIntegerFieldUpdater<WebSessionStat> requestIntervalHistogram_10000_100000_Updater = AtomicIntegerFieldUpdater.newUpdater(WebSessionStat.class,
              "requestIntervalHistogram_10000_100000");
      static final AtomicIntegerFieldUpdater<WebSessionStat> requestIntervalHistogram_100000_1000000_Updater = AtomicIntegerFieldUpdater.newUpdater(WebSessionStat.class,
              "requestIntervalHistogram_100000_1000000");
      static final AtomicIntegerFieldUpdater<WebSessionStat> requestIntervalHistogram_1000000_10000000_Updater = AtomicIntegerFieldUpdater.newUpdater(WebSessionStat.class,
              "requestIntervalHistogram_1000000_10000000");
      static final AtomicIntegerFieldUpdater<WebSessionStat> requestIntervalHistogram_10000000_more_Updater = AtomicIntegerFieldUpdater.newUpdater(WebSessionStat.class,
              "requestIntervalHistogram_10000000_more");
  
      public WebSessionStat(String sessionId) {
          super();
          this.sessionId = sessionId;
      }
  
      public void reset() {
          concurrentMaxUpdater.set(this, 0);
          requestCountUpdater.set(this, 0);
          requestErrorCountUpdater.set(this, 0);
          requestTimeNanoUpdater.set(this, 0);
  
          jdbcFetchRowCountUpdater.set(this, 0);
          jdbcUpdateCountUpdater.set(this, 0);
          jdbcExecuteCountUpdater.set(this, 0);
          jdbcExecuteTimeNanoUpdater.set(this, 0);
          jdbcCommitCountUpdater.set(this, 0);
          jdbcRollbackCountUpdater.set(this, 0);
  
          remoteAddresses = null;
          principal = null;
  
          requestIntervalHistogram_0_1_Updater.set(this, 0);
          requestIntervalHistogram_1_10_Updater.set(this, 0);
          requestIntervalHistogram_10_100_Updater.set(this, 0);
          requestIntervalHistogram_100_1000_Updater.set(this, 0);
          requestIntervalHistogram_1000_10000_Updater.set(this, 0);
          requestIntervalHistogram_10000_100000_Updater.set(this, 0);
          requestIntervalHistogram_100000_1000000_Updater.set(this, 0);
          requestIntervalHistogram_1000000_10000000_Updater.set(this, 0);
          requestIntervalHistogram_10000000_more_Updater.set(this, 0);
      }
  
      public String getUserAgent() {
          return userAgent;
      }
  
      public void setUserAgent(String userAgent) {
          this.userAgent = userAgent;
      }
  
      public String getSessionId() {
          return sessionId;
      }
  
      public long getCreateTimeMillis() {
          return createTimeMillis;
      }
  
      public String getPrincipal() {
          return principal;
      }
  
      public void setPrincipal(String principal) {
          this.principal = principal;
      }
  
      public void setCreateTimeMillis(long createTimeMillis) {
          this.createTimeMillis = createTimeMillis;
      }
  
      public long getLastAccessTimeMillis() {
          return lastAccessTimeMillis;
      }
  
      public String getRemoteAddress() {
          return remoteAddresses;
      }
  
      public void setLastAccessTimeMillis(long lastAccessTimeMillis) {
          if (this.lastAccessTimeMillis > 0) {
              long interval = lastAccessTimeMillis - this.lastAccessTimeMillis;
              requestIntervalHistogramRecord(interval);
          }
          this.lastAccessTimeMillis = lastAccessTimeMillis;
      }
  
      private void requestIntervalHistogramRecord(long nanoSpan) {
          long millis = nanoSpan / 1000 / 1000;
  
          if (millis < 1) {
              requestIntervalHistogram_0_1_Updater.incrementAndGet(this);
          } else if (millis < 10) {
              requestIntervalHistogram_1_10_Updater.incrementAndGet(this);
          } else if (millis < 100) {
              requestIntervalHistogram_10_100_Updater.incrementAndGet(this);
          } else if (millis < 1000) {
              requestIntervalHistogram_100_1000_Updater.incrementAndGet(this);
          } else if (millis < 10000) {
              requestIntervalHistogram_1000_10000_Updater.incrementAndGet(this);
          } else if (millis < 100000) {
              requestIntervalHistogram_10000_100000_Updater.incrementAndGet(this);
          } else if (millis < 1000000) {
              requestIntervalHistogram_100000_1000000_Updater.incrementAndGet(this);
          } else if (millis < 10000000) {
              requestIntervalHistogram_1000000_10000000_Updater.incrementAndGet(this);
          } else {
              requestIntervalHistogram_10000000_more_Updater.incrementAndGet(this);
          }
      }
  
      public void beforeInvoke() {
          int running = runningCountUpdater.incrementAndGet(this);
  
          for (; ; ) {
              int max = concurrentMaxUpdater.get(this);
              if (running > max) {
                  if (concurrentMaxUpdater.compareAndSet(this, max, running)) {
                      break;
                  }
              } else {
                  break;
              }
          }
  
          incrementRequestCount();
  
          WebRequestStat requestStat = WebRequestStat.current();
          if (requestStat != null) {
              this.setLastAccessTimeMillis(requestStat.getStartMillis());
          }
      }
  
      public void incrementRequestCount() {
          requestCountUpdater.incrementAndGet(this);
      }
  
      public void afterInvoke(Throwable error, long nanos) {
          runningCountUpdater.decrementAndGet(this);
          reacord(nanos);
      }
  
      public void reacord(long nanos) {
          requestTimeNanoUpdater.addAndGet(this, nanos);
  
          WebRequestStat requestStat = WebRequestStat.current();
          if (requestStat != null) {
              this.addJdbcExecuteCount(requestStat.getJdbcExecuteCount());
              this.addJdbcFetchRowCount(requestStat.getJdbcFetchRowCount());
              this.addJdbcUpdateCount(requestStat.getJdbcUpdateCount());
              this.addJdbcCommitCount(requestStat.getJdbcCommitCount());
              this.addJdbcRollbackCount(requestStat.getJdbcRollbackCount());
              this.addJdbcExecuteTimeNano(requestStat.getJdbcExecuteTimeNano());
          }
      }
  
      public void addRemoteAddress(String ip) {
          if (ip == null || ip.length() < 7) {
              return;
          }
          if (remoteAddresses == null) {
              this.remoteAddresses = ip;
              return;
          }
          //判断ip是否已经存在需要考虑几种情况
          //只有一个ip的情况
          if (remoteAddresses.equals(ip)) {
              return;
          }
          //是第一个ip的情况
          if (remoteAddresses.startsWith(ip + ';')) {
              return;
          }
          String addedIp = ';' + ip;
          //是最后一个ip的情况
          if (remoteAddresses.endsWith(addedIp)) {
              return;
          }
          //ip已经添加在中间的场景
          if (remoteAddresses.contains(';' + ip + ';')) {
              return;
          }
          if (remoteAddresses.length() > 256) {
              return;
          }
  
          remoteAddresses += addedIp;
      }
  
      public int getRunningCount() {
          return this.runningCount;
      }
  
      public long getConcurrentMax() {
          return concurrentMax;
      }
  
      public long getRequestCount() {
          return requestCount;
      }
  
      public long getRequestErrorCount() {
          return requestErrorCount;
      }
  
      public long getRequestTimeNano() {
          return requestTimeNano;
      }
  
      public void addJdbcFetchRowCount(long delta) {
          jdbcFetchRowCountUpdater.addAndGet(this, delta);
      }
  
      public long getJdbcFetchRowCount() {
          return jdbcFetchRowCount;
      }
  
      public void addJdbcUpdateCount(long updateCount) {
          jdbcUpdateCountUpdater.addAndGet(this, updateCount);
      }
  
      public long getJdbcUpdateCount() {
          return jdbcUpdateCount;
      }
  
      public void incrementJdbcExecuteCount() {
          jdbcExecuteCountUpdater.incrementAndGet(this);
      }
  
      public void addJdbcExecuteCount(long executeCount) {
          jdbcExecuteCountUpdater.addAndGet(this, executeCount);
      }
  
      public long getJdbcExecuteCount() {
          return jdbcExecuteCount;
      }
  
      public long getJdbcExecuteTimeNano() {
          return jdbcExecuteTimeNano;
      }
  
      public void addJdbcExecuteTimeNano(long nano) {
          jdbcExecuteTimeNanoUpdater.addAndGet(this, nano);
      }
  
      public void incrementJdbcCommitCount() {
          jdbcCommitCountUpdater.incrementAndGet(this);
      }
  
      public long getJdbcCommitCount() {
          return jdbcCommitCount;
      }
  
      public void addJdbcCommitCount(long commitCount) {
          jdbcCommitCountUpdater.addAndGet(this, commitCount);
      }
  
      public void incrementJdbcRollbackCount() {
          jdbcRollbackCountUpdater.incrementAndGet(this);
      }
  
      public long getJdbcRollbackCount() {
          return jdbcRollbackCount;
      }
  
      public void addJdbcRollbackCount(long rollbackCount) {
          jdbcRollbackCountUpdater.addAndGet(this, rollbackCount);
      }
  
      public long[] getRequestInterval() {
          return new long[]{
                  //
                  requestIntervalHistogram_0_1, //
                  requestIntervalHistogram_1_10, //
                  requestIntervalHistogram_10_100, //
                  requestIntervalHistogram_100_1000, //
                  requestIntervalHistogram_1000_10000, //
                  requestIntervalHistogram_10000_100000, //
                  requestIntervalHistogram_100000_1000000, //
                  requestIntervalHistogram_1000000_10000000, //
                  requestIntervalHistogram_10000000_more //
          };
      }
  
      public Map<String, Object> getStatData() {
          return getValue(false).getStatData();
      }
  
      public WebSessionStatValue getValue(boolean reset) {
          WebSessionStatValue val = new WebSessionStatValue();
  
          val.sessionId = sessionId;
          val.runningCount = this.getRunningCount();
          val.concurrentMax = get(this, concurrentMaxUpdater, reset);
          val.requestCount = get(this, requestCountUpdater, reset);
          val.requestErrorCount = get(this, requestErrorCountUpdater, reset);
          val.requestTimeNano = get(this, requestTimeNanoUpdater, reset);
          val.jdbcFetchRowCount = get(this, jdbcFetchRowCountUpdater, reset);
          val.jdbcUpdateCount = get(this, jdbcUpdateCountUpdater, reset);
          val.jdbcExecuteCount = get(this, jdbcExecuteCountUpdater, reset);
          val.jdbcExecuteTimeNano = get(this, jdbcExecuteTimeNanoUpdater, reset);
          val.jdbcCommitCount = get(this, jdbcCommitCountUpdater, reset);
          val.jdbcRollbackCount = get(this, jdbcRollbackCountUpdater, reset);
          val.createTimeMillis = createTimeMillis;
          val.lastAccessTimeMillis = lastAccessTimeMillis;
          val.remoteAddress = remoteAddresses;
          val.principal = principal;
          val.userAgent = userAgent;
  
          val.requestIntervalHistogram_0_1 = get(this, requestIntervalHistogram_0_1_Updater, reset);
          val.requestIntervalHistogram_1_10 = get(this, requestIntervalHistogram_1_10_Updater, reset);
          val.requestIntervalHistogram_10_100 = get(this, requestIntervalHistogram_10_100_Updater, reset);
          val.requestIntervalHistogram_100_1000 = get(this, requestIntervalHistogram_100_1000_Updater, reset);
          val.requestIntervalHistogram_1000_10000 = get(this, requestIntervalHistogram_1000_10000_Updater, reset);
          val.requestIntervalHistogram_10000_100000 = get(this, requestIntervalHistogram_10000_100000_Updater, reset);
          val.requestIntervalHistogram_100000_1000000 = get(this, requestIntervalHistogram_100000_1000000_Updater, reset);
          val.requestIntervalHistogram_1000000_10000000 = get(this, requestIntervalHistogram_1000000_10000000_Updater,
                  reset);
          val.requestIntervalHistogram_10000000_more = get(this, requestIntervalHistogram_10000000_more_Updater, reset);
  
          return val;
      }
  }
  