All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.diirt.support.pva.rpcservice.rpcclient.RPCClientPool Maven / Gradle / Ivy

/**
 * Copyright (C) 2010-18 diirt developers. See COPYRIGHT.TXT
 * All rights reserved. Use is subject to license terms. See LICENSE.TXT
 */
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.diirt.support.pva.rpcservice.rpcclient;

import org.epics.pvaccess.client.rpc.RPCClient;
import org.epics.pvaccess.server.rpc.RPCRequestException;
import org.epics.pvdata.pv.Status;

import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Implementation of a simple rpc client pool.
 * The Pool uses a {@link PoolProperties} object for storing all the meta information about the rpc client pool.
 * As the underlying implementation, the rpc client pool uses {@link java.util.concurrent.BlockingQueue} to store active and idle rpc clients.
 *
 * @author Filip Hanik (tomcat connection pool)
 * @author dkumar (modified for a rpc client pool implementation)
 */

class RPCClientPool implements PoolStatistics {

  /**
   * Logger
   */
  private final static Logger log = Logger.getLogger(RPCClientPool.class.getName());

  /**
   * Carries the size of the pool, instead of relying on a queue implementation
   * that usually iterates over to get an exact count
   */
  private AtomicInteger size = new AtomicInteger(0);

  /**
   * All the information about the pool
   * These are the properties the pool got instantiated with
   */
  private PoolConfiguration poolProperties;

  /**
   * Contains all the rpc clients that are in use
   * TODO - this shouldn't be a blocking queue, simply a list to hold our objects
   */
  private BlockingQueue busy;

  /**
   * Contains all the idle rpc clients
   */
  private BlockingQueue idle;

  /**
   * The thread that is responsible for checking abandoned and idle threads
   */
  private volatile PoolCleaner poolCleaner;

  /**
   * Pool closed flag
   */
  private volatile boolean closed = false;

  //RPCClientPool is a pool of rpc clients with the same host name and client name
  private final String hostName;
  private final String channelName;

  /**
   * counter to track how many threads are waiting for a rpc client
   */
  private AtomicInteger waitcount = new AtomicInteger(0);


  /**
   * Instantiate a rpc client pool. This will create rpc clients if initialSize is larger than 0.
   * The {@link PoolProperties} should not be reused for another rpc client pool.
   *
   * @param prop PoolProperties - all the properties for this rpc client pool
   * @throws RPCRequestException
   */
  public RPCClientPool(PoolConfiguration prop, String hostName, String channelName) throws RPCRequestException {
    //setup quick access variables and pools
    this.hostName = hostName;
    this.channelName = channelName;
    init(prop);
  }


  /**
   * Borrows a rpc client from the pool. If a rpc client is available (in the
   * idle queue) or the pool has not reached {@link PoolProperties#maxActive
   * maxActive} rpc clients, a rpc client is returned immediately. If no
   * rpc clients is available, the pool will attempt to fetch a rpc client for
   * {@link PoolProperties#maxWait maxWait} milliseconds.
   *
   * @return RPCClient - a PooledRPCClient object wrapping the real rpc client.
   * @throws RPCRequestException - if the wait times out or a failure occurs creating a rpc client
   */
  public RPCClient getRpcClient() throws RPCRequestException {
    // check out a rpc client
    return  borrowRpcClient(-1);
  }


  /**
   * Returns the name of this pool
   *
   * @return String - the name of the pool
   */
  @Override
  public String getName() {
    return "RPCClientPool(" + this.hostName + "-" + this.channelName+")";
  }


  /**
   * Return the number of threads waiting for a rpc client
   *
   * @return number of threads waiting for a rpc client
   */
  @Override
  public int getWaitCount() {
    return waitcount.get();
  }


  /**
   * Returns the pool properties associated with this rpc client pool
   *
   * @return PoolProperties
   */
  @Override
  public PoolConfiguration getPoolProperties() {
    return this.poolProperties;
  }


  /**
   * Returns the total size of this pool, this includes both busy and idle rpc clients
   *
   * @return int - number of established rpc clients
   */
  @Override
  public int getSize() {
    return size.get();
  }


  /**
   * Returns the number of rpc clients that are in use
   *
   * @return int - number of established rpc clients that are being used by the application
   */
  @Override
  public int getActive() {
    return busy.size();
  }


  /**
   * Returns the number of idle rpc clients
   *
   * @return int - number of established rpc clients not being used
   */
  @Override
  public int getIdle() {
    return idle.size();
  }


  /**
   * Returns true if {@link #close close} has been called, and the rpc client pool is unusable
   *
   * @return boolean
   */
  @Override
  public boolean isClosed() {
    return this.closed;
  }


  /**
   * Get pool's host name
   * @return host name
   */
  @Override
  public String getHostName() {
    return this.hostName;
  }


  /**
   * Get pool's rpc client name
   * @return rpc client name
   */
  @Override
  public String getChannelName() {
    return channelName;
  }


  /**
   * Closes the pool and all disconnects all idle rpc clients
   * Active rpc clients will be closed upon the {@link org.epics.pvaccess.client.rpc.RPCClient#destroy()} method is called
   * on the underlying rpc client instead of being returned to the pool
   *
   * @param force - true to even close the active rpc client
   */
  protected void close(boolean force) {
    //are we already closed
    if (this.closed) return;
    //prevent other threads from entering
    this.closed = true;
    //stop background thread
    if (poolCleaner != null) {
      poolCleaner.stopRunning();
    }

    /* release all idle rpc clients */
    BlockingQueue pool = (idle.size() > 0) ? idle : (force ? busy : idle);
    while (pool.size() > 0) {
      try {
        //retrieve the next rpc client
        PooledRPCClient rpcClient = pool.poll(1000, TimeUnit.MILLISECONDS);
        //close it and retrieve the next one, if one is available
        while (rpcClient != null) {
          //close the rpc client
          if (pool == idle)
            release(rpcClient);
          else
            abandon(rpcClient);
          rpcClient = pool.poll(1000, TimeUnit.MILLISECONDS);
        } //while
      } catch (InterruptedException ex) {
        Thread.interrupted();
      }
      if (pool.size() == 0 && force && pool != busy) pool = busy;
    }
  } //closePool


  /**
   * Initialize the rpc client pool - called from the constructor
   *
   * @param properties PoolProperties - properties used to initialize the pool with
   * @throws RPCRequestException if initialization fails
   */
  protected void init(PoolConfiguration properties) throws RPCRequestException {

    poolProperties = properties;

    //make sure the pool is properly configured
    if (properties.getMaxActive() < 1) {
      log.log(Level.WARNING, "maxActive is smaller than 1, setting maxActive to: " + PoolProperties.DEFAULT_MAX_ACTIVE);
      properties.setMaxActive(PoolProperties.DEFAULT_MAX_ACTIVE);
    }
    if (properties.getMaxActive() properties.getMaxActive()) {
      log.log(Level.WARNING, "minIdle is larger than maxActive, setting minIdle to: " + properties.getMaxActive());
      properties.setMinIdle(properties.getMaxActive());
    }
    if (properties.getMaxIdle() > properties.getMaxActive()) {
      log.log(Level.WARNING, "maxIdle is larger than maxActive, setting maxIdle to: " + properties.getMaxActive());
      properties.setMaxIdle(properties.getMaxActive());
    }
    if (properties.getMaxIdle() < properties.getMinIdle()) {
      log.log(Level.WARNING, "maxIdle is smaller than minIdle, setting maxIdle to: " + properties.getMinIdle());
      properties.setMaxIdle(properties.getMinIdle());
    }

    busy = new ArrayBlockingQueue(properties.getMaxActive(), false);

    idle = new ArrayBlockingQueue(properties.getMaxActive(), false);


    //if the evictor thread is supposed to run, start it now
    if (properties.isPoolSweeperEnabled()) {
      poolCleaner = new PoolCleaner("[Pool-Cleaner]:" + this.hostName + "-"
        + this.channelName, this, properties.getTimeBetweenEvictionRunsMillis());
      poolCleaner.start();
    }


    //initialize the pool with its initial set of members
    PooledRPCClient[] initialPool = new PooledRPCClient[poolProperties.getInitialSize()];

    try {
      for (int i = 0; i < initialPool.length; i++) {
        initialPool[i] = this.borrowRpcClient(0); //don't wait, should be no contention
      } //for

    } catch (RPCRequestException x) {
      close(true);
      throw x;
    } finally {
      //return the members as idle to the pool
      for (int i = 0; i < initialPool.length; i++) {
        if (initialPool[i] != null) {
          try {this.returnRpcClient(initialPool[i]);}catch(Exception x){/*NOOP*/}
        } //end if
      } //for
    } //catch
    closed = false;
  }


  /**
   * thread safe way to abandon a rpc client
   * signals a rpc client to be abandoned.
   * this will disconnect the rpc client, and log the stack trace if logAbanded=true
   *
   * @param rpcClient PooledRPCClient
   */
  protected void abandon(PooledRPCClient rpcClient) {
    if (rpcClient == null)
      return;
    try {
      rpcClient.lock();
      String trace = rpcClient.getStackTrace();
      if (getPoolProperties().isLogAbandoned()) {
        log.log(Level.WARNING, "Channel has been abandoned " + rpcClient + ":" + trace);
      }
      //release the rpc client
      release(rpcClient);
    } finally {
      rpcClient.unlock();
    }
  }


  /**
   * thread safe way to abandon a rpc client
   * signals a rpc client to be abandoned.
   * this will disconnect the rpc client, and log the stack trace if logAbanded=true
   *
   * @param rpcClient PooledRPCClient
   */
  protected void suspect(PooledRPCClient rpcClient) {
    if (rpcClient == null)
      return;
    if (rpcClient.isSuspect())
      return;
    try {
      rpcClient.lock();
      String trace = rpcClient.getStackTrace();
      if (getPoolProperties().isLogAbandoned()) {
        log.log(Level.WARNING, "Channel has been marked suspect, possibly abandoned " + rpcClient + "[" + (System.currentTimeMillis() - rpcClient.getTimestamp()) + " ms.]:" + trace);
      }

      rpcClient.setSuspect(true);
    } finally {
      rpcClient.unlock();
    }
  }


  /**
   * thread safe way to release a rpc client
   *
   * @param rpcClient PooledRPCClient
   */
  protected void release(PooledRPCClient rpcClient) {
    if (rpcClient == null)
      return;
    try {
      rpcClient.lock();
      if (rpcClient.release()) {
        //counter only decremented once
        size.addAndGet(-1);
      }
    } finally {
      rpcClient.unlock();
    }
    // we've asynchronously reduced the number of rpc clients
    // we could have threads stuck in idle.poll(timeout) that will never be
    // notified
    if (waitcount.get() > 0) {
      idle.offer(create(true));
    }
  }


  /**
   * Thread safe way to retrieve a rpc client from the pool
   *
   * @param wait - time to wait, overrides the maxWait from the properties,
   *             set to -1 if you wish to use maxWait, 0 if you wish no wait time.
   * @return PooledRPCClient
   * @throws RPCRequestException
   */
  private PooledRPCClient borrowRpcClient(int wait) throws RPCRequestException {

    if (isClosed()) {
      throw new RPCRequestException(Status.StatusType.ERROR, "Channel pool closed.");
    } //end if

    //get the current time stamp
    long now = System.currentTimeMillis();
    //see if there is one available immediately
    PooledRPCClient rpcClient = idle.poll();

    while (true) {
      if (rpcClient != null) {
        //configure the rpc client and return it
        PooledRPCClient result = borrowRpcClient(now, rpcClient);
        //null should never be returned, but was in a previous impl.
        if (result != null) return result;
      }

      //if we get here, see if we need to create one
      //this is not 100% accurate since it doesn't use a shared
      //atomic variable - a rpc client can become idle while we are creating
      //a new rpc client
      if (size.get() < getPoolProperties().getMaxActive()) {
        //atomic duplicate check
        if (size.addAndGet(1) > getPoolProperties().getMaxActive()) {
          //if we got here, two threads passed through the first if
          size.decrementAndGet();
        } else {
          //create a rpc client, we're below the limit
          return createRpcClient(now, rpcClient);
        }
      } //end if

      //calculate wait time for this iteration
      long maxWait = wait;
      //if the passed in wait time is -1, means we should use the pool property value
      if (wait == -1) {
        maxWait = (getPoolProperties().getMaxWait() <= 0) ? Long.MAX_VALUE : getPoolProperties().getMaxWait();
      }

      long timetowait = Math.max(0, maxWait - (System.currentTimeMillis() - now));
      waitcount.incrementAndGet();
      try {
        //retrieve an existing rpc client
        rpcClient = idle.poll(timetowait, TimeUnit.MILLISECONDS);
      } catch (InterruptedException ex) {
        Thread.interrupted();//clear the flag, and bail out
        RPCRequestException sx = new RPCRequestException(Status.StatusType.ERROR, "Pool wait interrupted.");
        sx.initCause(ex);
        throw sx;
      } finally {
        waitcount.decrementAndGet();
      }
      if (maxWait == 0 && rpcClient == null) { //no wait, return one if we have one
        throw new RPCRequestException(Status.StatusType.ERROR, "[" + Thread.currentThread().getName() + "] " +
          "NoWait: Pool empty. Unable to fetch a channel, none available[" + busy.size() + " in use].");
      }
      //we didn't get a rpc client, lets see if we timed out
      if (rpcClient == null) {
        if ((System.currentTimeMillis() - now) >= maxWait) {
          throw new RPCRequestException(Status.StatusType.ERROR, "[" + Thread.currentThread().getName() + "] " +
            "Timeout: Pool empty. Unable to fetch a channel in " + (maxWait / 1000) +
            " seconds, none available[" + busy.size() + " in use].");
        } else {
          //no timeout, lets try again
          continue;
        }
      }
    } //while
  }


  /**
   * Creates a rpc client.
   *
   * @param now     timestamp of when this was called
   * @param notUsed Argument not used
   * @return a PooledRPCClient that has been connected
   * @throws RPCRequestException
   */
  protected PooledRPCClient createRpcClient(long now, PooledRPCClient notUsed) throws RPCRequestException {

    //no rpc client where available we'll create one
    PooledRPCClient rpcClient = create(false);

    boolean error = false;
    try {
      //connect and validate the rpc client
      rpcClient.lock();
      rpcClient.connect();
      if (rpcClient.validate(PooledRPCClient.VALIDATE_INIT)) {
        //no need to lock a new one, its not contented
        rpcClient.setTimestamp(now);
        if (getPoolProperties().isLogAbandoned()) {
          rpcClient.setStackTrace(getThreadDump());
        }
        if (!busy.offer(rpcClient)) {
          log.log(Level.FINE, "Channel doesn't fit into busy array, channel will not be traceable.");
        }
        return rpcClient;
      } else {
        //validation failed, make sure we disconnect
        //and clean up
        error = true;
      } //end if
    } catch (Exception e) {
      error = true;
      log.log(Level.FINE, "Unable to create a new ChannelRPC.", e);
      if (e instanceof RPCRequestException) {
        throw (RPCRequestException) e;
      } else {
        RPCRequestException ex = new RPCRequestException(Status.StatusType.ERROR, e.getMessage());
        ex.initCause(e);
        throw ex;
      }
    } finally {
      // rpc client can never be null here
      if (error) {
        release(rpcClient);
      }
      rpcClient.unlock();
    }//catch
    return null;
  }

  /**
   * Validates and configures a previously idle rpc client
   *
   * @param now - timestamp
   * @param rpcClient - the rpc client to validate and configure
   * @return ch
   * @throws RPCRequestException if a validation error happens
   */
  protected PooledRPCClient borrowRpcClient(long now, PooledRPCClient rpcClient) throws RPCRequestException {
    //we have a rpc client, lets set it up

    //flag to see if we need to nullify
    boolean setToNull = false;
    try {
      rpcClient.lock();


      if (rpcClient.isReleased()) {
        return null;
      }

      if (!rpcClient.isDiscarded() && !rpcClient.isInitialized()) {
        //attempt to connect
        rpcClient.connect();
      }

      if ((!rpcClient.isDiscarded()) && rpcClient.validate(PooledRPCClient.VALIDATE_BORROW)) {
        //set the timestamp
        rpcClient.setTimestamp(now);
        if (getPoolProperties().isLogAbandoned()) {
          //set the stack trace for this pool
          rpcClient.setStackTrace(getThreadDump());
        }
        if (!busy.offer(rpcClient)) {
          log.log(Level.FINE, "Channel doesn't fit into busy array, channel will not be traceable.");
        }
        return rpcClient;
      }
      //if we reached here, that means the rpc client
      //is either has another principal, is discarded or validation failed.
      //we will make one more attempt
      //in order to guarantee that the thread that just acquired
      //the rpc client shouldn't have to poll again.
      try {
        rpcClient.reconnect();
        if (rpcClient.validate(PooledRPCClient.VALIDATE_INIT)) {
          //set the timestamp
          rpcClient.setTimestamp(now);
          if (getPoolProperties().isLogAbandoned()) {
            //set the stack trace for this pool
            rpcClient.setStackTrace(getThreadDump());
          }
          if (!busy.offer(rpcClient)) {
            log.log(Level.FINE, "Channel doesn't fit into busy array, channel will not be traceable.");
          }
          return rpcClient;
        } else {
          //validation failed.
          release(rpcClient);
          setToNull = true;
          throw new RPCRequestException(Status.StatusType.ERROR, "Failed to validate a newly established channel.");
        }
      } catch (Exception x) {
        release(rpcClient);
        setToNull = true;
        if (x instanceof RPCRequestException) {
          throw (RPCRequestException) x;
        } else {
          RPCRequestException ex = new RPCRequestException(Status.StatusType.ERROR, x.getMessage());
          ex.initCause(x);
          throw ex;
        }
      }
    } finally {
      rpcClient.unlock();
      if (setToNull) {
        rpcClient = null;
      }
    }
  }


  /**
   * Determines if a rpc client should be closed upon return to the pool.
   *
   * @param ch    - the rpc client
   * @param action - the validation action that should be performed
   * @return true if the rpc client should be closed
   */
  protected boolean shouldClose(PooledRPCClient ch, int action) {
    if (ch.isDiscarded()) return true;
    if (isClosed()) return true;
    if (!ch.validate(action)) return true;
    if (getPoolProperties().getMaxAge() > 0) {
      return (System.currentTimeMillis() - ch.getLastConnected()) > getPoolProperties().getMaxAge();
    } else {
      return false;
    }
  }


  /**
   * Returns a rpc client to the pool
   * If the pool is closed, the rpc client will be released
   * If the rpc client is not part of the busy queue, it will be released.
   * If {@link PoolProperties#testOnReturn} is set to true it will be validated
   *
   * @param ch PooledRPCClient to be returned to the pool
   */
  protected void returnRpcClient(PooledRPCClient ch) {
    if (isClosed()) {
      //if the rpc client pool is closed
      //close the rpc client instead of returning it
      release(ch);
      return;
    } //end if

    if (ch != null) {
      try {
        ch.lock();

        if (busy.remove(ch)) {

          if (!shouldClose(ch, PooledRPCClient.VALIDATE_RETURN)) {
            ch.setStackTrace(null);
            ch.setTimestamp(System.currentTimeMillis());
            if (((idle.size() >= poolProperties.getMaxIdle()) && !poolProperties.isPoolSweeperEnabled()) || (!idle.offer(ch))) {
              log.log(Level.FINE, "Channel [" + ch + "] will be closed and not returned to the pool, idle["
                + idle.size() + "]>=maxIdle[" + poolProperties.getMaxIdle() + "] idle.offer failed.");

              release(ch);
            }
          } else {
            log.log(Level.FINE, "Channel [" + ch + "] will be closed and not returned to the pool.");

            release(ch);
          } //end if
        } else {
          log.log(Level.FINE, "Channel [" + ch + "] will be closed and not returned to the pool, busy.remove failed.");

          release(ch);
        }
      } finally {
        ch.unlock();
      }
    } //end if
  } //checkIn


  /**
   * Determines if a rpc client should be abandoned based on
   * {@link PoolProperties#abandonWhenPercentageFull} setting.
   *
   * @return true if the rpc client should be abandoned
   */
  protected boolean shouldAbandon() {
    if (poolProperties.getAbandonWhenPercentageFull() == 0) return true;
    float used = busy.size();
    float max = poolProperties.getMaxActive();
    float perc = poolProperties.getAbandonWhenPercentageFull();
    return (used / max * 100f) >= perc;
  }

  /**
   * Iterates through all the busy rpc clients and checks for rpc clients that have timed out
   */
  public void checkAbandoned() {
    try {
      if (busy.size() == 0) return;
      Iterator locked = busy.iterator();
      int sto = getPoolProperties().getSuspectTimeout();
      while (locked.hasNext()) {
        PooledRPCClient ch = locked.next();
        boolean setToNull = false;
        try {
          ch.lock();
          //the rpc client has been returned to the pool
          //ignore it
          if (idle.contains(ch))
            continue;
          long time = ch.getTimestamp();
          long now = System.currentTimeMillis();
          if (shouldAbandon() && (now - time) > ch.getAbandonTimeout()) {
            busy.remove(ch);
            abandon(ch);
            setToNull = true;
          } else if (sto > 0 && (now - time) > (sto * 1000)) {
            suspect(ch);
          } else {
            //do nothing
          } //end if
        } finally {
          ch.unlock();
          if (setToNull)
            ch = null;
        }
      } //while
    } catch (ConcurrentModificationException e) {
      log.log(Level.WARNING, "checkAbandoned failed.", e);
    } catch (Exception e) {
      log.log(Level.WARNING, "checkAbandoned failed, it will be retried.", e);
    }
  }

  /**
   * Iterates through the idle rpc clients and resizes the idle pool based on parameters
   * {@link PoolProperties#maxIdle}, {@link PoolProperties#minIdle}, {@link PoolProperties#minEvictableIdleTimeMillis}
   */
  public void checkIdle() {
    try {
      if (idle.size() == 0) return;
      long now = System.currentTimeMillis();
      Iterator unlocked = idle.iterator();
      while ((idle.size() >= getPoolProperties().getMinIdle()) && unlocked.hasNext()) {
        PooledRPCClient ch = unlocked.next();
        boolean setToNull = false;
        try {
          ch.lock();
          //the rpc client been taken out, we can't clean it up
          if (busy.contains(ch))
            continue;
          long time = ch.getTimestamp();
          if ((ch.getReleaseTime() > 0) && ((now - time) > ch.getReleaseTime()) &&
            (getSize() > getPoolProperties().getMinIdle())) {
            release(ch);
            idle.remove(ch);
            setToNull = true;
          } else {
            //do nothing
          } //end if
        } finally {
          ch.unlock();
          if (setToNull)
            ch = null;
        }
      } //while
    } catch (ConcurrentModificationException e) {
      log.log(Level.FINE, "checkIdle failed.", e);
    } catch (Exception e) {
      log.log(Level.FINE, "checkIdle failed, it will be retried.", e);
    }

  }


  /**
   * Forces a validation of all idle rpc client if {@link PoolProperties#testWhileIdle} is set.
   */
  public void testAllIdle() {
    try {
      if (idle.size() == 0) return;
      Iterator unlocked = idle.iterator();
      while (unlocked.hasNext()) {
        PooledRPCClient ch = unlocked.next();
        try {
          ch.lock();
          //the rpc client been taken out, we can't clean it up
          if (busy.contains(ch))
            continue;
          if (!ch.validate(PooledRPCClient.VALIDATE_IDLE)) {
            idle.remove(ch);
            release(ch);
          }
        } finally {
          ch.unlock();
        }
      } //while
    } catch (ConcurrentModificationException e) {
      log.log(Level.WARNING, "testAllIdle failed.", e);
    } catch (Exception e) {
      log.log(Level.WARNING, "testAllIdle failed, it will be retried.", e);
    }

  }


  /**
   * Creates a stack trace representing the existing thread's current state.
   *
   * @return a string object representing the current state.
   */
  protected static String getThreadDump() {
    Exception x = new Exception();
    x.fillInStackTrace();
    return getStackTrace(x);
  }


  /**
   * Convert an exception into a String
   *
   * @param x - the throwable
   * @return a string representing the stack trace
   */
  public static String getStackTrace(Throwable x) {
    if (x == null) {
      return null;
    } else {
      java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
      java.io.PrintStream writer = new java.io.PrintStream(bout);
      x.printStackTrace(writer);
      String result = bout.toString();
      return (x.getMessage() != null && x.getMessage().length() > 0) ? x.getMessage() + ";" + result : result;
    } //end if
  }


  /**
   * Create a new pooled rpc client  object. Not connected nor validated.
   *
   * @return a pooled rpc client rpc object
   */
  protected PooledRPCClient create(boolean incrementCounter) {
    if (incrementCounter) size.incrementAndGet();
    PooledRPCClient rpcClient = new PooledRPCClient(getPoolProperties(), this);
    return rpcClient;
  }


  /**
   * Hook to perform final actions on a pooled rpc client object once it has been disconnected and will be discarded
   *
   * @param rpcClient
   */
  protected void finalize(PooledRPCClient rpcClient) {
  }


  /**
   * Hook to perform final actions on a pooled rpc client object once it has been disconnected and will be discarded
   *
   * @param rpcClient
   */
  protected void disconnectEvent(PooledRPCClient rpcClient, boolean finalizing) {
  }


  protected class PoolCleaner extends Thread {
    protected RPCClientPool pool;
    protected long sleepTime;
    protected volatile boolean run = true;

    PoolCleaner(String name, RPCClientPool pool, long sleepTime) {
      super(name);
      this.setDaemon(true);
      this.pool = pool;
      this.sleepTime = sleepTime;
      if (sleepTime <= 0) {
        log.log(Level.WARNING, "channel pool evicter thread interval is set to 0, defaulting to 30 seconds");
        this.sleepTime = 1000 * 30;
      } else if (sleepTime < 1000) {
        log.log(Level.WARNING, "channel pool evicter thread interval is set to lower than 1 second.");
      }
    }

    @Override
    public void run() {
      while (run) {
        try {
          sleep(sleepTime);
        } catch (InterruptedException e) {
          // ignore it
          Thread.interrupted();
          continue;
        } //catch

        if (pool.isClosed()) {
          if (pool.getSize() <= 0) {
            run = false;
          }
        } else {
          try {
            if (pool.getPoolProperties().isRemoveAbandoned())
              pool.checkAbandoned();
            if (pool.getPoolProperties().getMinIdle() < pool.idle.size())
              pool.checkIdle();
            if (pool.getPoolProperties().isTestWhileIdle())
              pool.testAllIdle();
          } catch (Exception x) {
            log.log(Level.SEVERE,"", x);
          } //catch
        } //end if
      } //while
    } //run


    public void stopRunning() {
      run = false;
      interrupt();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy