/*
 *  eXist Open Source Native XML Database
 *  Copyright (C) 2001-04 Wolfgang M. Meier
 *  wolfgang@exist-db.org
 *  http://exist-db.org
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 *  $Id$
 */
package org.exist.storage;

import java.util.Iterator;
import java.util.Stack;

import org.apache.log4j.Logger;
import org.exist.source.Source;
import org.exist.util.Configuration;
import org.exist.util.hashtable.Object2ObjectHashMap;
import org.exist.xquery.CompiledXQuery;
import org.exist.xquery.XQueryContext;

/**
 * Global pool for pre-compiled XQuery expressions. Expressions are
 * stored and retrieved from the pool by comparing the {@link org.exist.source.Source}
 * objects from which they were created. For each XQuery, a maximum of 
 * {@link #MAX_STACK_SIZE} compiled expressions are kept in the pool.
 * An XQuery expression will be removed from the pool if it has not been
 * used for a pre-defined timeout. These settings can be configured in conf.xml.
 * 
 * @author wolf
 */
public class XQueryPool extends Object2ObjectHashMap {

    public final static int MAX_STACK_SIZE = 5;

    public final static long TIMEOUT = 120000;

    public final static long TIMEOUT_CHECK_INTERVAL = 30000;

    private final static Logger LOG = Logger.getLogger(XQueryPool.class);

    private long lastTimeOutCheck;

    private int maxStackSize;

    private long timeout;

    private long timeoutCheckInterval;

    /**
     * @param conf
     */
    public XQueryPool(Configuration conf) {
        super(27);
        lastTimeOutCheck = System.currentTimeMillis();

        Integer maxStSz = (Integer) conf
                .getProperty("db-connection.query-pool.max-stack-size");
        Long t = (Long) conf.getProperty("db-connection.query-pool.timeout");
        Long tci = (Long) conf
                .getProperty("db-connection.query-pool.timeout-check-interval");

        if (maxStSz != null)
            maxStackSize = maxStSz.intValue();
        else
            maxStackSize = MAX_STACK_SIZE;

        if (t != null)
            timeout = t.longValue();
        else
            timeout = TIMEOUT;

        if (tci != null)
            timeoutCheckInterval = tci.longValue();
        else
            timeoutCheckInterval = TIMEOUT_CHECK_INTERVAL;

        LOG.info("QueryPool: maxStackSize = " + maxStackSize + "; timeout = "
                + timeout + "; timeoutCheckInterval = " + timeoutCheckInterval);

    }

    public synchronized void returnCompiledXQuery(Source source,
            CompiledXQuery xquery) {
        Stack stack = (Stack) get(source);
        if (stack == null) {
            stack = new Stack();
            source.setCacheTimestamp(System.currentTimeMillis());
            put(source, stack);
        }
        if (stack.size() < maxStackSize) {
            stack.push(xquery);
        }
        timeoutCheck();
    }

    public synchronized CompiledXQuery borrowCompiledXQuery(DBBroker broker, Source source) {
        int idx = getIndex(source);
        if (idx < 0)
            return null;
        Source key = (Source) keys[idx];
        int validity = key.isValid(broker);
        if (validity == Source.UNKNOWN)
            validity = key.isValid(source);
        if (validity == Source.INVALID || validity == Source.UNKNOWN) {
            keys[idx] = REMOVED;
            values[idx] = null;
            LOG.debug(source.getKey() + " is invalid");
            return null;
        }
        Stack stack = (Stack) values[idx];
        if (stack != null && !stack.isEmpty()) {
            // now check if the compiled expression is valid
            // it might become invalid if an imported module has changed.
            CompiledXQuery query = (CompiledXQuery) stack.pop();
            XQueryContext context = query.getContext();
            context.setBroker(broker);
            if (!query.isValid()) {
                // the compiled query is no longer valid: one of the imported
                // modules may have changed
                remove(key);
                return null;
            } else
                return query;
        }
        return null;
    }

    private void timeoutCheck() {
        final long currentTime = System.currentTimeMillis();
        if (currentTime - lastTimeOutCheck < timeoutCheckInterval)
            return;

        for (Iterator i = iterator(); i.hasNext();) {
            Source next = (Source) i.next();
            if (currentTime - next.getCacheTimestamp() > timeout)
                remove(next);
        }
    }
}

@@ -1731,16 +1731,69 @@
 hMap %7B%0A%0A
+    public final static int MAX_POOL_SIZE = 32;%0A    %0A
     publ
@@ -1832,16 +1832,16 @@
 ZE = 5;%0A
-
 %0A    pub
@@ -3264,24 +3264,66 @@
 y xquery) %7B%0A
+        if (size() %3C MAX_POOL_SIZE) %7B%0A    
         Stac
@@ -3353,32 +3353,36 @@
 ource);%0A        
+    
 if (stack == nul
@@ -3382,24 +3382,28 @@
  == null) %7B%0A
+    
             
@@ -3435,16 +3435,20 @@
         
+   
+ 
 source.s
@@ -3505,16 +3505,20 @@
         
+    
 put(sour
@@ -3529,32 +3529,36 @@
 stack);%0A        
+    
 %7D%0A        if (st
@@ -3543,32 +3543,36 @@
       %7D%0A        
+ 
+   
 if (stack.size()
@@ -3598,24 +3598,28 @@
             
+    
 stack.push(x
@@ -3622,24 +3622,38 @@
 sh(xquery);%0A
+            %7D%0A
         %7D%0A  
@@ -5291,16 +5291,18 @@
 timeout)
+ %7B
 %0A       
@@ -5324,16 +5324,30 @@
 (next);%0A
+            %7D%0A
         

Set a maximum size for the xquery pool.