org.talend.dataquality.common.inference.ConcurrentAnalyzer.java Source code

Java tutorial

Introduction

Here is the source code for org.talend.dataquality.common.inference.ConcurrentAnalyzer.java

Source

// ============================================================================
//
// Copyright (C) 2006-2016 Talend Inc. - www.talend.com
//
// This source code is available under agreement available at
// %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt
//
// You should have received a copy of the agreement
// along with this program; if not, write to Talend SA
// 9 rue Pages 92150 Suresnes, France
//
// ============================================================================
package org.talend.dataquality.common.inference;

import java.util.List;

import org.apache.commons.pool.KeyedObjectPool;
import org.apache.commons.pool.KeyedPoolableObjectFactory;
import org.apache.commons.pool.impl.GenericKeyedObjectPool;

/**
 * A {@link Analyzer} implementation that allows use of an Analyzer pool by several threads. Please note analyzer
 * instance is <b>only</b> returned to the pool on {@link #close()} call.
 */
public class ConcurrentAnalyzer<T> implements Analyzer<T> {

    private static final long serialVersionUID = 6896234073310039985L;

    private final ThreadLocal<Analyzer<T>> threadLocal;

    private ConcurrentAnalyzer(int maxSize, AnalyzerSupplier<Analyzer<T>> supplier) {
        GenericKeyedObjectPool.Config config = new GenericKeyedObjectPool.Config();
        config.maxTotal = maxSize;
        config.maxActive = maxSize;
        config.maxIdle = maxSize / 2;
        config.minIdle = maxSize / 2;
        config.maxWait = -1;
        // #1: Initialize a ThreadLocal backed with a generic object pool -> allows getting previously borrowed instance
        // and return to pool on remove() call.
        // #2: Pool is expected to be thread safe.
        final KeyedObjectPool<Thread, Analyzer<T>> pool = new GenericKeyedObjectPool<>(new Factory<>(supplier),
                config);
        this.threadLocal = new ThreadLocal<Analyzer<T>>() {

            @Override
            protected Analyzer<T> initialValue() {
                try {
                    return pool.borrowObject(Thread.currentThread());
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void remove() {
                try {
                    // Order matters here as remove() causes get() to return null.
                    pool.returnObject(Thread.currentThread(), get());
                } catch (Exception e) {
                    throw new RuntimeException(e);
                } finally {
                    // Thread local keeps a lot of references, make sure everything gets cleaned up on error.
                    super.remove();
                }
            }
        };
    }

    public static <T> Analyzer<T> make(AnalyzerSupplier<Analyzer<T>> supplier, int maxSize) {
        return new ConcurrentAnalyzer<>(maxSize, supplier);
    }

    @Override
    public void init() {
        Analyzer<T> analyzer = threadLocal.get();
        analyzer.init();
    }

    @Override
    public boolean analyze(String... record) {
        Analyzer<T> analyzer = threadLocal.get();
        return analyzer.analyze(record);
    }

    @Override
    public void end() {
        Analyzer<T> analyzer = threadLocal.get();
        analyzer.end();
    }

    @Override
    public List<T> getResult() {
        Analyzer<T> analyzer = threadLocal.get();
        return analyzer.getResult();
    }

    @Override
    public Analyzer<T> merge(Analyzer<T> another) {
        Analyzer<T> analyzer = threadLocal.get();
        return analyzer.merge(another);
    }

    @Override
    public void close() throws Exception {
        // Return previously borrowed instance to pool
        threadLocal.remove();
    }

    private static class Factory<T> implements KeyedPoolableObjectFactory<Thread, Analyzer<T>> {

        private final AnalyzerSupplier<Analyzer<T>> supplier;

        public Factory(AnalyzerSupplier<Analyzer<T>> supplier) {
            this.supplier = supplier;
        }

        @Override
        public synchronized Analyzer<T> makeObject(Thread key) throws Exception {
            return supplier.get();
        }

        @Override
        public void destroyObject(Thread key, Analyzer<T> obj) throws Exception {
            // do nothing
        }

        @Override
        public boolean validateObject(Thread key, Analyzer<T> obj) {
            return true;
        }

        @Override
        public void activateObject(Thread key, Analyzer<T> obj) throws Exception {
            // do nothing
        }

        @Override
        public void passivateObject(Thread key, Analyzer<T> obj) throws Exception {
            // do nothing
        }
    }

}