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.pool;
  
  import com.alibaba.druid.pool.DruidPooledPreparedStatement.PreparedStatementKey;
  import com.alibaba.druid.proxy.jdbc.CallableStatementProxy;
  import com.alibaba.druid.proxy.jdbc.PreparedStatementProxy;
  import com.alibaba.druid.support.logging.Log;
  import com.alibaba.druid.support.logging.LogFactory;
  import com.alibaba.druid.util.OracleUtils;
  
  import java.sql.PreparedStatement;
  import java.sql.SQLException;
  import java.util.Iterator;
  import java.util.LinkedHashMap;
  import java.util.Map;
  import java.util.Map.Entry;
  
  /**
   * @author wenshao [szujobs@hotmail.com]
   */
  public class PreparedStatementPool {
      private static final Log LOG = LogFactory.getLog(PreparedStatementPool.class);
  
      private final LRUCache map;
      private final DruidAbstractDataSource dataSource;
  
      public PreparedStatementPool(DruidConnectionHolder holder) {
          this.dataSource = holder.getDataSource();
          int initCapacity = holder.getDataSource().getMaxPoolPreparedStatementPerConnectionSize();
          if (initCapacity <= 0) {
              initCapacity = 16;
          }
          map = new LRUCache(initCapacity);
      }
  
      public static enum MethodType {
          M1, M2, M3, M4, M5, M6, Precall_1, Precall_2, Precall_3
      }
  
      public PreparedStatementHolder get(PreparedStatementKey key) throws SQLException {
          PreparedStatementHolder holder = map.get(key);
  
          if (holder != null) {
              if (holder.isInUse() && (!dataSource.isSharePreparedStatements())) {
                  return null;
              }
  
              holder.incrementHitCount();
              dataSource.incrementCachedPreparedStatementHitCount();
              if (holder.isEnterOracleImplicitCache()) {
                  OracleUtils.exitImplicitCacheToActive(holder.statement);
              }
          } else {
              dataSource.incrementCachedPreparedStatementMissCount();
          }
  
          return holder;
      }
  
      public void remove(PreparedStatementHolder stmtHolder) throws SQLException {
          if (stmtHolder == null) {
              return;
          }
          map.remove(stmtHolder.key);
          closeRemovedStatement(stmtHolder);
      }
  
      public void put(PreparedStatementHolder stmtHolder) throws SQLException {
          PreparedStatement stmt = stmtHolder.statement;
  
          if (stmt == null) {
              return;
          }
  
          if (dataSource.isOracle() && dataSource.isUseOracleImplicitCache()) {
              OracleUtils.enterImplicitCache(stmt);
              stmtHolder.setEnterOracleImplicitCache(true);
          } else {
              stmtHolder.setEnterOracleImplicitCache(false);
          }
  
          PreparedStatementHolder oldStmtHolder = map.put(stmtHolder.key, stmtHolder);
  
          if (oldStmtHolder == stmtHolder) {
              return;
          }
  
          if (oldStmtHolder != null) {
              oldStmtHolder.setPooling(false);
              closeRemovedStatement(oldStmtHolder);
          } else {
              if (stmtHolder.getHitCount() == 0) {
                  dataSource.incrementCachedPreparedStatementCount();
              }
          }
  
          stmtHolder.setPooling(true);
  
          if (LOG.isDebugEnabled()) {
              String message = null;
              if (stmtHolder.statement instanceof PreparedStatementProxy) {
                  PreparedStatementProxy stmtProxy = (PreparedStatementProxy) stmtHolder.statement;
                  if (stmtProxy instanceof CallableStatementProxy) {
                      message = "{conn-" + stmtProxy.getConnectionProxy().getId() + ", cstmt-" + stmtProxy.getId()
                              + "} enter cache";
                  } else {
                      message = "{conn-" + stmtProxy.getConnectionProxy().getId() + ", pstmt-" + stmtProxy.getId()
                              + "} enter cache";
                  }
              } else {
                  message = "stmt enter cache";
              }
  
              LOG.debug(message);
          }
      }
  
      public void clear() {
          Iterator<Entry<PreparedStatementKey, PreparedStatementHolder>> iter = map.entrySet().iterator();
          while (iter.hasNext()) {
              Entry<PreparedStatementKey, PreparedStatementHolder> entry = iter.next();
  
              closeRemovedStatement(entry.getValue());
  
              iter.remove();
          }
      }
  
      public void closeRemovedStatement(PreparedStatementHolder holder) {
          if (LOG.isDebugEnabled()) {
              String message = null;
              if (holder.statement instanceof PreparedStatementProxy) {
                  PreparedStatementProxy stmtProxy = (PreparedStatementProxy) holder.statement;
                  if (stmtProxy instanceof CallableStatementProxy) {
                      message = "{conn-" + stmtProxy.getConnectionProxy().getId() + ", cstmt-" + stmtProxy.getId()
                              + "} exit cache";
                  } else {
                      message = "{conn-" + stmtProxy.getConnectionProxy().getId() + ", pstmt-" + stmtProxy.getId()
                              + "} exit cache";
                  }
              } else {
                  message = "stmt exit cache";
              }
  
              LOG.debug(message);
          }
  
          holder.setPooling(false);
          if (holder.isInUse()) {
              return;
          }
  
          if (holder.isEnterOracleImplicitCache()) {
              try {
                  OracleUtils.exitImplicitCacheToClose(holder.statement);
              } catch (Exception ex) {
                  LOG.error("exitImplicitCacheToClose error", ex);
              }
          }
          dataSource.closePreapredStatement(holder);
      }
  
      public Map<PreparedStatementKey, PreparedStatementHolder> getMap() {
          return map;
      }
  
      public int size() {
          return this.map.size();
      }
  
      public class LRUCache extends LinkedHashMap<PreparedStatementKey, PreparedStatementHolder> {
          private static final long serialVersionUID = 1L;
  
          public LRUCache(int maxSize) {
              super(maxSize, 0.75f, true);
          }
  
          protected boolean removeEldestEntry(Entry<PreparedStatementKey, PreparedStatementHolder> eldest) {
              boolean remove = (size() > dataSource.getMaxPoolPreparedStatementPerConnectionSize());
  
              if (remove) {
                  closeRemovedStatement(eldest.getValue());
              }
  
              return remove;
          }
      }
  }
  