/*
 * BaseProvider.java
 *
 * Generated by {productTitle}  {versionName} - {utilityTitle}
 *
 * {copywrite}
 */
package {packageName}.common;

import java.util.concurrent.Executor;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import javax.cim.CIMInstance;
import javax.cim.CIMObjectPath;
import javax.wbem.CloseableIterator;
import javax.wbem.WBEMException;
import javax.wbem.WBEMOperation;
import javax.wbem.WBEMOperationErrors;
import javax.wbem.provider.IndicationProvider;
import javax.wbem.provider.ProviderHandle;
import javax.wbem.provider.PullInstanceProvider;

import com.ws.wbem.CloseableAddableIterator;
import com.ws.wbem.jserver.DaemonThreadFactory;
import com.ws.wbem.jserver.JServerConstants;
import com.ws.wbem.query.fql.FQLParser;

public abstract class BaseProvider implements PullInstanceProvider,
		IndicationProvider {

	// Default value for number of threads
	private static int DEFAULT_NUM_THREADS = 10;
	// Get value from prop file, if it is in there
	private static int NUM_THREADS;

	/* Save a handle to the WBEM Server. */
	protected static volatile ProviderHandle mHandle;
	
	// TODO: Use the J Server log, modify this to use a different log file
	protected static final Logger mLogger = Logger
			.getLogger(JServerConstants.JSERVER_LOGGER_NAME);
	
    protected static volatile Executor exec = null;

    public BaseProvider() {
        // temp variable
        Executor tExec = exec;
        // see if the thread pool has been created
        if (null == tExec) {
            // not yet, synchronize on this class
            synchronized (BaseProvider.class) {
                tExec = exec;
                // make sure no one else created it while we waited for lock
                if (null == tExec) {
                    // Determine the number of threads for the pool
                    NUM_THREADS = DEFAULT_NUM_THREADS;
                    // create the pool
                    tExec = new ThreadPoolExecutor(1, NUM_THREADS, 10L,
                            TimeUnit.SECONDS, new SynchronousQueue<Runnable>(),
                            new DaemonThreadFactory());
                    ((ThreadPoolExecutor) tExec).allowCoreThreadTimeOut(true);
                    exec = tExec;
                }
            }
        }
    }

	@Override
		public void activateFilter(CIMInstance filter,
			CIMInstance listenerDestination, CIMInstance subscription)
			throws WBEMException {
		getImplementation().activateFilter(filter, listenerDestination,
				subscription);
	}

	@Override
	public void authorizeFilter(CIMInstance filter,
			CIMInstance listenerDestination, CIMInstance subscription)
			throws WBEMException {
		getImplementation().authorizeFilter(filter, listenerDestination,
				subscription);
	}

	@Override
	public void close() throws WBEMException {
		getImplementation().close();
	}

	@Override
	public CIMObjectPath createInstance(CIMInstance ci) throws WBEMException {
		return getImplementation().createInstance(ci);
	}

	@Override
		public void deActivateFilter(CIMInstance filter,
			CIMInstance listenerDestination, CIMInstance subscription)
			throws WBEMException {
		getImplementation().deActivateFilter(filter, listenerDestination,
				subscription);
	}

	@Override
	public void deleteInstance(CIMObjectPath op) throws WBEMException {
		getImplementation().deleteInstance(op);
	}

	protected void enumerate(CloseableAddableIterator<CIMInstance> iter,
			CIMObjectPath op, String[] propertyList,
			String filterQueryLanguage, String filterQuery,
			boolean continueOnError) throws WBEMException {
		// create FQLParser object. This will verify the FilterQuery and
		// FilterQueryLanguage values. This will be used later to determine
		// if CIMInstance objects match the the query
		final FQLParser fqlParser = FQLParser.getFQLParser(filterQueryLanguage,
				filterQuery, op, WBEMOperation.ENUMERATEINSTANCES);
		final BaseProviderImpl theImpl = getImplementation();
		theImpl.getAllInstances(iter, op, propertyList, fqlParser,
				WBEMOperation.ENUMERATEINSTANCES);
	}

	@Override
	public CloseableIterator<CIMInstance> enumerateInstances(CIMObjectPath pOp,
			String[] propertyList, String filterQueryLanguage,
			String filterQuery, boolean continueOnError) throws WBEMException {
		return threadedEnumerate(pOp, propertyList, filterQueryLanguage,
				filterQuery, continueOnError);
	}

	@Override
	public CloseableIterator<CIMInstance> execQuery(CIMObjectPath op,
			String query, String ql) throws WBEMException {
		return threadedExecQuery(op, query, ql);
	}
	
	/**
	 * Returns an object for the implementation
	 * 
	 * @return The instrumentation to use
	 * @throws WBEMException
	 *             If the implementation is not set
	 */
	abstract protected BaseProviderImpl getImplementation()
			throws WBEMException;
			
	@Override
	public CIMInstance getInstance(CIMObjectPath op, String[] propertyList)
			throws WBEMException {
		return getImplementation().getInstance(op, propertyList);
	}
	
	/**
	 * Initialization assigns a provider handle to the WBEM Server so the
	 * provider can communicate with the WBEM Server and access the repository,
	 * and initial values to the provider's properties. <BR>
	 * <BR>
	 */
	@Override
	public void initialize(ProviderHandle handle) throws WBEMException {
        if (null == mHandle) {
            synchronized (BaseProvider.class) {
                if (null == mHandle) {
                    mHandle = handle;
                }
            }
        }
		getImplementation().initialize(handle);
	}
	
	@Override
	public void modifyInstance(CIMInstance ci, String[] propertyList)
			throws WBEMException {
		getImplementation().modifyInstance(ci, propertyList);
	}

	protected CloseableIterator<CIMInstance> threadedEnumerate(
			final CIMObjectPath op, final String[] propertyList,
			final String filterQueryLanguage, final String filterQuery,
			final boolean continueOnError) throws WBEMException {
		final CloseableAddableIterator<CIMInstance> iter = new CloseableAddableIterator<CIMInstance>();

		final Runnable enumerateThread = new Runnable() {
			@Override
			public void run() {
				try {
					enumerate(iter, op, propertyList, filterQueryLanguage,
							filterQuery, continueOnError);
				} catch (final Throwable t) {
					WBEMException we = iter.getWBEMException();
					// iterator should already have the exception set.
					// lets check that it is set, if it isn't then set it.
					if (null == we) {
						we = WBEMOperationErrors.getOtherFailureException(op,
								t, WBEMOperation.ENUMERATEINSTANCES, """);
						// not set! lets put exception into iterator
						iter.setException(we);
						// close the iterator
						iter.close();
					}
					
				}
			}
		};
		// add thread to executor (can not be null)
		exec.execute(enumerateThread);
		
		return iter;
	}

	protected CloseableIterator<CIMInstance> threadedExecQuery(
			final CIMObjectPath op, final String query, final String ql) {
		final CloseableAddableIterator<CIMInstance> iter = new CloseableAddableIterator<CIMInstance>();
		final Runnable enumerateThread = new Runnable() {
		    @SuppressWarnings("deprecation")
			@Override
			public void run() {
				try {
					getImplementation().execQuery(iter, op, query, ql);
				} catch (final Throwable t) {
					WBEMException we = iter.getWBEMException();
					// iterator should already have the exception set.
					// lets check that it is set, if it isn't then set it.
					if (null == we) {
						we = WBEMOperationErrors.getOtherFailureException(op,
								t, WBEMOperation.EXECUTEQUERY, """);
						// not set! lets put exception into iterator
						iter.setException(we);
						// close the iterator
						iter.close();
					}

				}
			}
		};
		// add thread to executor (can not be null)
		exec.execute(enumerateThread);

		return iter;
	}
	
}
