ResourcePooler.java 14.1 KB
/*
 * Decompiled with CFR 0_118.
 * 
 * Could not load the following classes:
 *  com.adobe.jmx.statistics.BoundedRangeStatistic
 *  com.adobe.jmx.statistics.CountStatistic
 *  com.adobe.jmx.statistics.RangeStatistic
 *  com.adobe.jmx.statistics.StatisticsHost
 *  com.adobe.jmx.statistics.TimeStatistic
 *  org.slf4j.Logger
 *  org.slf4j.LoggerFactory
 */
package com.adobe.service;

import com.adobe.jmx.statistics.*;
import com.adobe.service.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.ListIterator;
import java.util.Map;

class ResourcePooler {
    public static final boolean tracePoolerActivity = System.getProperty("com.adobe.bmc.tracePoolerActivity") != null && System.getProperty("com.adobe.bmc.tracePoolerActivity").trim().equalsIgnoreCase("true");
    private static final Logger logger = LoggerFactory.getLogger(ResourcePooler.class);
    private static final int MAX_STRIKES = 5;
    private static int strikes = 0;
    private ArrayList pool = new ArrayList();
    private boolean initializingResource = false;
    private ConnectionFactoryManager connectionFactoryManager;
    BoundedRangeStatistic poolSize;
    RangeStatistic queueLength;
    RangeStatistic inUse;
    CountStatistic requestCount;
    TimeStatistic holdTime;
    private boolean isShutdown = false;
    private WaitItem waitQueueTail = null;
    private static long nextSeq = 0;

    ResourcePooler(ConnectionFactoryManager connectionFactoryMgr) {
        this.connectionFactoryManager = connectionFactoryMgr;
        ServiceAPI host = this.connectionFactoryManager.service;
        this.poolSize = BoundedRangeStatistic.create((StatisticsHost)host, (String)"PoolSize");
        this.poolSize.setBounds(0, 0);
        this.queueLength = RangeStatistic.create((StatisticsHost)host, (String)"QueueLength");
        this.inUse = RangeStatistic.create((StatisticsHost)host, (String)"InUse");
        this.requestCount = CountStatistic.create((StatisticsHost)host, (String)"RequestCount");
        this.holdTime = TimeStatistic.create((StatisticsHost)host, (String)"HoldTime");
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    ConnectionResource allocateResource() {
        WaitItem waitItem = null;
        while (!this.isShutdown) {
            Object resource;
            ConnectionResource resource2;
            logger.debug("*****" + Thread.currentThread().getName() + " trying to obtain lock on " + this + " for resource allocation");
            ResourcePooler resourcePooler = this;
            synchronized (resourcePooler) {
                if (this.connectionFactoryManager.service.getState() == ServiceAPI.SERVICE_FAILED) {
                    if (strikes < 5) {
                        logger.info("Failure while process launch, attempt: " + ++strikes);
                    } else {
                        strikes = 0;
                        throw new IllegalStateException("Connection to failed service.");
                    }
                }
                if (this.pool.size() > 0) {
                    ListIterator iter = this.pool.listIterator();
                    while (iter.hasNext()) {
                        resource = (ConnectionResource)iter.next();
                        ResourcePeer peer = resource.peer;
                        if (!peer.allocated) {
                            peer.allocated = true;
                            strikes = 0;
                            peer.setReusable(false);
                            peer.requestStartTime = this.holdTime.begin();
                            this.inUse.increment();
                            this.requestCount.increment();
                            logger.debug("*****" + Thread.currentThread().getName() + " allocated " + resource + ", Pool: " + ResourcePooler.getPoolStateMap(this) + ", initializing=" + this.initializingResource + ", poolsize=" + this.pool.size() + ", max=" + this.poolSize.getUpperBound());
                            return resource;
                        }
                        logger.debug("*****" + Thread.currentThread().getName() + " " + resource + " is already allocated" + ", Pool: " + ResourcePooler.getPoolStateMap(this) + ", initializing=" + this.initializingResource + ", poolsize=" + this.pool.size() + ", max=" + this.poolSize.getUpperBound());
                    }
                }
                long max = this.poolSize.getUpperBound();
                if (this.initializingResource || max != 0 && (long)this.pool.size() >= max) {
                    logger.debug("*****" + Thread.currentThread().getName() + " waiting for resource; initializing=" + this.initializingResource + ", poolsize=" + this.pool.size() + ", max=" + max);
                    waitItem = this.waitForResource(waitItem);
                    continue;
                }
                this.initializingResource = true;
            }
            try {
                resource2 = this.connectionFactoryManager.createConnectionResource();
            }
            catch (RuntimeException e) {
                logger.warn("Unexpected error creating connection resource", (Throwable)e);
                resource = this;
                synchronized (resource) {
                    this.initializingResource = false;
                }
                throw e;
            }
            ResourcePeer peer = resource2.peer;
            peer.pooler = this;
            peer.setReusable(true);
            logger.debug("*****" + Thread.currentThread().getName() + " created resource " + resource2 + ", now check for readiness");
            resource = this;
            synchronized (resource) {
                if (resource2.peer.ready) {
                    ResourcePooler.notifyReady(resource2);
                } else {
                    logger.debug("*****" + Thread.currentThread().getName() + " newly created resource " + resource2 + " not ready yet; initializing=" + this.initializingResource + ", poolsize=" + this.pool.size() + ", max=" + this.poolSize.getUpperBound());
                }
                continue;
            }
        }
        throw new IllegalStateException("Service is shutdown");
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    void resetResources() {
        ResourcePooler resourcePooler = this;
        synchronized (resourcePooler) {
            ListIterator iter = this.pool.listIterator();
            while (iter.hasNext()) {
                Resource resource = (Resource)iter.next();
                logger.info("Shutting down resource: " + resource);
                resource.onShutdown();
            }
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public void onShutdown() {
        logger.debug("Pooler {} for service {} shutting down", (Object)this, (Object)this.connectionFactoryManager.service.getName());
        ResourcePooler resourcePooler = this;
        synchronized (resourcePooler) {
            this.isShutdown = true;
            this.NotifyAllInQueue();
        }
        resourcePooler = this;
        synchronized (resourcePooler) {
            ListIterator iter = this.pool.listIterator();
            while (iter.hasNext()) {
                Resource resource = (Resource)iter.next();
                resource.onShutdown();
            }
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    static void onDeallocate(Resource resource) {
        ResourcePeer peer = resource.peer;
        ResourcePooler pooler = peer.pooler;
        if (pooler != null) {
            ResourcePooler resourcePooler = pooler;
            synchronized (resourcePooler) {
                if (peer.requestStartTime != 0) {
                    pooler.inUse.decrement();
                    pooler.holdTime.end(peer.requestStartTime);
                    peer.requestStartTime = 0;
                }
                peer.allocated = false;
                if (!peer.reusable) {
                    if (resource instanceof ProcessResource) {
                        ((ProcessResource)resource).releaseAgent();
                    }
                    pooler.pool.remove(resource);
                    pooler.poolSize.setCurrent((long)pooler.pool.size());
                }
                if (!peer.ready) {
                    pooler.initializingResource = false;
                    pooler.connectionFactoryManager.service.setState(ServiceAPI.SERVICE_FAILED);
                    logger.debug("*****" + Thread.currentThread().getName() + " deallocated *FAILED RESOURCE* " + resource + ", Pool: " + ResourcePooler.getPoolStateMap(pooler) + ", initializing=" + pooler.initializingResource + ", poolsize=" + pooler.pool.size() + ", max=" + pooler.poolSize.getUpperBound() + ", service " + pooler.connectionFactoryManager.service.getName() + " marked FAILED.");
                    pooler.NotifyAllInQueue();
                } else {
                    logger.debug("*****" + Thread.currentThread().getName() + " deallocated " + resource + ", Pool: " + ResourcePooler.getPoolStateMap(pooler) + ", initializing=" + pooler.initializingResource + ", poolsize=" + pooler.pool.size() + ", max=" + pooler.poolSize.getUpperBound());
                    pooler.NotifyFirstInQueue();
                }
            }
        } else {
            peer.allocated = false;
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    static void notifyReady(Resource resource) {
        ResourcePeer peer = resource.peer;
        ResourcePooler pooler = peer.pooler;
        if (pooler != null) {
            ResourcePooler resourcePooler = pooler;
            synchronized (resourcePooler) {
                pooler.initializingResource = false;
                if (!pooler.pool.contains(resource)) {
                    pooler.pool.add(resource);
                    pooler.poolSize.setCurrent((long)pooler.pool.size());
                }
                logger.debug("*****" + Thread.currentThread().getName() + " resource " + resource + " notifying readiness, Pool: " + ResourcePooler.getPoolStateMap(pooler) + ", initializing=" + pooler.initializingResource + ", poolsize=" + pooler.pool.size() + ", max=" + pooler.poolSize.getUpperBound());
                pooler.NotifyAllInQueue();
            }
        }
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private WaitItem waitForResource(WaitItem waitItem) {
        if (waitItem == null) {
            waitItem = new WaitItem(nextSeq++);
            if (this.waitQueueTail == null) {
                waitItem.next = waitItem;
                this.waitQueueTail = waitItem;
            } else {
                waitItem.next = this.waitQueueTail.next;
                this.waitQueueTail.next = waitItem;
                this.waitQueueTail = waitItem;
            }
        } else if (this.waitQueueTail == null) {
            waitItem.next = waitItem;
            this.waitQueueTail = waitItem;
        } else {
            WaitItem item = this.waitQueueTail.next;
            while (item != this.waitQueueTail && item.next.seq < waitItem.seq) {
                item = item.next;
            }
            waitItem.next = item.next;
            item.next = waitItem;
            if (item == this.waitQueueTail) {
                this.waitQueueTail = waitItem;
            }
        }
        try {
            this.queueLength.increment();
            this.wait();
        }
        catch (InterruptedException e) {
            try {
                logger.debug("Interrupted waiting for resource", (Throwable)e);
            }
            catch (Throwable var3_5) {
                this.queueLength.decrement();
                if (waitItem.next != null) {
                    WaitItem prev = this.waitQueueTail;
                    while (prev.next != waitItem) {
                        prev = prev.next;
                    }
                    prev.next = waitItem.next;
                    if (this.waitQueueTail == waitItem) {
                        this.waitQueueTail = null;
                    }
                    waitItem.next = null;
                }
                throw var3_5;
            }
            this.queueLength.decrement();
            if (waitItem.next != null) {
                WaitItem prev = this.waitQueueTail;
                while (prev.next != waitItem) {
                    prev = prev.next;
                }
                prev.next = waitItem.next;
                if (this.waitQueueTail == waitItem) {
                    this.waitQueueTail = null;
                }
                waitItem.next = null;
            }
        }
        this.queueLength.decrement();
        if (waitItem.next != null) {
            WaitItem prev = this.waitQueueTail;
            while (prev.next != waitItem) {
                prev = prev.next;
            }
            prev.next = waitItem.next;
            if (this.waitQueueTail == waitItem) {
                this.waitQueueTail = null;
            }
            waitItem.next = null;
        }
        return waitItem;
    }

    private synchronized void NotifyFirstInQueue() {
        if (this.waitQueueTail != null) {
            WaitItem item = this.waitQueueTail.next;
            this.waitQueueTail.next = item.next;
            if (item == this.waitQueueTail) {
                this.waitQueueTail = null;
            }
            item.next = null;
            item.thread.interrupt();
        }
    }

    private synchronized void NotifyAllInQueue() {
        while (this.waitQueueTail != null) {
            this.NotifyFirstInQueue();
        }
    }

    private static Map getPoolStateMap(ResourcePooler pooler) {
        HashMap<ConnectionResource, Boolean> poolState = new HashMap<ConnectionResource, Boolean>();
        for (int i = 0; i < pooler.pool.size(); ++i) {
            ConnectionResource cr = (ConnectionResource)pooler.pool.get(i);
            poolState.put(cr, cr.peer.allocated);
        }
        return poolState;
    }

    private static class WaitItem {
        final long seq;
        final Thread thread;
        WaitItem next;

        WaitItem(long nextSeq) {
            this.seq = nextSeq;
            this.thread = Thread.currentThread();
        }
    }

}