/*
 * Copyright 2005-2006 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package org.apache.jmeter.functions;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import org.xml.sax.SAXException;

/**
 * This class wraps the XPathFileContainer for use across multiple threads.
 * 
 * It maintains a list of nodelist containers, one for each file/xpath combination
 * 
 */
public class XPathWrapper {

	transient private static Logger log = LoggingManager.getLoggerForClass();

    private XPathWrapper() {// Prevent separate instantiation
        super();
    }

	/* The cache of file packs */
	private static ThreadLocal filePacks = new ThreadLocal() {
		protected Object initialValue() {
			return new HashMap();
		}
	};

	private static XPathFileContainer open(String file, String xpathString) {
		log.info("Opening " + file);
		XPathFileContainer frcc=null;
		try {
            frcc = new XPathFileContainer(file, xpathString);
		} catch (FileNotFoundException e) {
			log.warn(e.getLocalizedMessage());
		} catch (IOException e) {
			log.warn(e.getLocalizedMessage());
		} catch (ParserConfigurationException e) {
			log.warn(e.getLocalizedMessage());
		} catch (SAXException e) {
			log.warn(e.getLocalizedMessage());
		} catch (TransformerException e) {
			log.warn(e.getLocalizedMessage());
		}
        return frcc;
	}

    /**
     * Not thread-safe - must be called from a synchronized method.
     * 
     * @param file
     * @param xpathString
     * @return
     */
    public static String getXPathString(String file, String xpathString) {
		Map my = (Map) filePacks.get();
        String key = file+xpathString;
        XPathFileContainer xpfc = (XPathFileContainer) my.get(key);
		if (xpfc == null) // First call
		{
			xpfc=open(file, xpathString);
			// TODO improve the error handling
			if (xpfc == null) {
				log.error("XPathWrapper is null!");
				return "";
			}
            my.put(key,xpfc);
		}
        int currentRow = xpfc.nextRow();
        log.debug("getting match number " + currentRow);
        return xpfc.getXPathString(currentRow);
	}

	public static void clearAll() {
		log.debug("clearAll()");
		Map my = (Map) filePacks.get();
        my.clear();
	}
}

@@ -1276,87 +1276,214 @@
     
-private XPathWrapper() %7B// Prevent separ
+/*%0A     * This Map serves two purposes:%0A     * - maps names to  containers%0A     * - ensures only one container per file across all threads%0A     */%0A    priv
 ate 
-in
 sta
-n
 ti
-ation%0A        super
+c Map fileContainers = new HashMap
 ();%0A    
 %7D%0A%0A%09
@@ -1478,18 +1478,16 @@
 ();%0A    
-%7D%0A
 %0A%09/* The
@@ -1507,16 +1507,42 @@
 e packs 
+- for faster local access 
 */%0A%09priv
@@ -1666,16 +1666,102 @@
 %09%7D%0A%09%7D;%0A%0A
+    private XPathWrapper() %7B// Prevent separate instantiation%0A        super();%0A    %7D%0A%0A
 %09private
@@ -1823,24 +1823,81 @@
 thString) %7B%0A
+        String tname = Thread.currentThread().getName();%0A
 %09%09log.info(%22
@@ -1895,17 +1895,25 @@
 og.info(
-%22
+tname+%22: 
 Opening 
@@ -2816,55 +2816,448 @@
  // 
-First call%0A%09%09%7B%0A%09%09%09xpfc=open(file, xpathString);
+We don't have a local copy%0A%09%09%7B%0A            synchronized(fileContainers)%7B%0A                xpfc = (XPathFileContainer) fileContainers.get(key);%0A                if (xpfc == null) %7B // There's no global copy either%0A                    xpfc=open(file, xpathString);                    %0A                %7D%0A                if (xpfc != null) %7B%0A                    fileContainers.put(key, xpfc);// save the global copy%0A                %7D%0A            %7D
 %0A%09%09%09
@@ -3403,16 +3403,39 @@
 y,xpfc);
+ // save our local copy
 %0A%09%09%7D%0A   
@@ -3695,13 +3695,150 @@
 lear();%0A
+        String tname = Thread.currentThread().getName();%0A        log.info(tname+%22: clearing container%22);%0A        fileContainers.clear();%0A
 %09%7D%0A%7D%0A

Use static Map to ensure same nodelist is used for all threads