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

org.carrot2.util.pool.SoftUnboundedPool Maven / Gradle / Ivy

Go to download

Carrot2 search results clustering framework. Minimal functional subset (core algorithms and infrastructure, no document sources).

There is a newer version: 3.16.3
Show newest version

/*
 * Carrot2 project.
 *
 * Copyright (C) 2002-2019, Dawid Weiss, Stanisław Osiński.
 * All rights reserved.
 *
 * Refer to the full license file "carrot2.LICENSE"
 * in the root folder of the repository checkout or at:
 * http://www.carrot2.org/carrot2.LICENSE
 */

package org.carrot2.util.pool;

import java.lang.ref.SoftReference;
import java.util.*;
import java.util.Map.Entry;

import org.carrot2.util.Pair;

import org.carrot2.shaded.guava.common.collect.Maps;

/**
 * An extremely simple, unbounded object pool. The pool can provide objects of may types,
 * objects get created using parameterless constructors. The pool holds objects using
 * {@link SoftReference}s, so they can be garbage collected when memory is needed.
 */
public final class SoftUnboundedPool implements IParameterizedPool
{
    private Map, P>, List>> instances = Maps.newHashMap();

    private IInstantiationListener instantiationListener;
    private IActivationListener activationListener;
    private IPassivationListener passivationListener;
    private IDisposalListener disposalListener;

    public void init(IInstantiationListener objectInstantiationListener,
        IActivationListener objectActivationListener,
        IPassivationListener objectPassivationListener,
        IDisposalListener objectDisposalListener)
    {
        this.instantiationListener = objectInstantiationListener;
        this.activationListener = objectActivationListener;
        this.passivationListener = objectPassivationListener;
        this.disposalListener = objectDisposalListener;
    }
    
    @SuppressWarnings("unchecked")
    public  I borrowObject(Class clazz, P parameter)
        throws InstantiationException, IllegalAccessException
    {
        I instance = null;
        synchronized (this)
        {
            if (instances == null)
            {
                throw new IllegalStateException("The pool has already been disposed of");
            }

            final Pair, P> key = new Pair, P>(
                clazz, parameter);
            List> list = instances.get(key);
            if (list == null)
            {
                list = new ArrayList>();
                instances.put(key, list);
            }

            while (list.size() > 0 && instance == null)
            {
                // This cast goes unchecked and can be broken by bad calls, but we shift
                // the responsibility to the users of this class.
                instance = (I) list.remove(0).get();
            }
        }

        // Not a problem that many threads create new objects for now.
        if (instance == null)
        {
            instance = clazz.newInstance();
            if (instantiationListener != null)
            {
                instantiationListener.objectInstantiated(instance, parameter);
            }
        }

        if (activationListener != null)
        {
            activationListener.activate(instance, parameter);
        }

        return instance;
    }

    public void returnObject(T object, P parameter)
    {
        if (object == null)
        {
            return;
        }

        if (passivationListener != null)
        {
            passivationListener.passivate(object, parameter);
        }

        synchronized (this)
        {
            if (instances == null)
            {
                return;
            }

            @SuppressWarnings({
                "rawtypes", "unchecked"
            })
            final Pair key = new Pair(object.getClass(), parameter);
            final List> list = instances.get(key);
            if (list == null)
            {
                throw new IllegalStateException(
                    "Returning an object that was never borrowed: " + object);
            }

            // The object must not be on the list at this point. The pool won't be large
            // enough for the linear scan to be a problem.
            for (SoftReference reference : list)
            {
                final T o = reference.get();
                if (o != null && o == object)
                {
                    throw new IllegalStateException("Object has not been borrowed");
                }
            }
            
            list.add(new SoftReference(object));
        }
    }

    public void dispose()
    {
        synchronized (this)
        {
            if (this.instances == null)
            {
                return;
            }
            
            Map, P>, List>> instancesRef = this.instances;
            this.instances = null;

            for (Entry, P>, List>> entry : instancesRef.entrySet())
            {
                for (SoftReference reference : entry.getValue())
                {
                    T instance = reference.get();
                    if (instance != null && disposalListener != null)
                    {
                        disposalListener.dispose(instance, entry.getKey().objectB);
                    }
                }
            }
        }
    }
}