package org.apache.commons.jcs;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

import java.util.concurrent.atomic.AtomicInteger;

import junit.framework.TestCase;

import org.apache.commons.jcs.access.GroupCacheAccess;
import org.apache.commons.jcs.access.exception.CacheException;

/**
 * Test Case for JCS-73, modeled after the Groovy code by Alexander Kleymenov
 *
 * @author Thomas Vandahl
 *
 */
public class JCSConcurrentCacheAccessUnitTest extends TestCase
{
    private final static int THREADS = 10;
    private final static int LOOPS = 10000;

    /**
     * the cache instance
     */
    protected GroupCacheAccess<Integer, String> cache;

    /**
     * the group name
     */
    protected String group = "group";

    /**
     * the error count
     */
    protected AtomicInteger errcount;

    @Override
	protected void setUp() throws Exception
	{
        super.setUp();
        JCS.setConfigFilename( "/TestJCS-73.ccf" );
        cache = JCS.getGroupCacheInstance( "cache" );
        errcount = new AtomicInteger(0);
	}

    @Override
    protected void tearDown()
        throws Exception
    {
        super.tearDown();
        cache.clear();
        cache.dispose();
    }

    /**
     * Worker thread
     */
    protected class Worker extends Thread
    {
    	@Override
		public void run()
		{
			String name = getName();

			for (int idx = 0; idx < LOOPS; idx++)
			{
				if (idx > 0)
				{
					// get previously stored value
		            String res = cache.getFromGroup(Integer.valueOf(idx-1), group);

		            if (res == null)
		            {
		                // null value got inspite of the fact it was placed in cache!
		                System.out.println("ERROR: for " + idx + " in " + name);
		                errcount.incrementAndGet();

		                // try to get the value again:
		                int n = 5;
		                while (n-- > 0)
		                {
		                    res = cache.getFromGroup(Integer.valueOf(idx-1), group);
		                    if (res != null)
		                    {
		                        // the value finally appeared in cache
		                    	System.out.println("ERROR FIXED for " + idx + ": " + res + " " + name);
		                    	errcount.decrementAndGet();
		                        break;
		                    }

		                    System.out.println("ERROR STILL PERSISTS for " + idx + " in " + name);
		                    try
		                    {
								Thread.sleep(1000);
							}
		                    catch (InterruptedException e)
							{
								// continue
							}
		                }
		            }

		            assertEquals("Values do not match", String.valueOf(idx-1), res);
				}

				 // put value in the cache
		        try
		        {
					cache.putInGroup(Integer.valueOf(idx), group, String.valueOf(idx));
				}
		        catch (CacheException e)
		        {
		        	// continue
				}

//		        if ((idx % 1000) == 0)
//		        {
//		        	System.out.println(name + " " + idx);
//		        }
			}

		}
    }

	/**
     *
     * @throws Exception
     */
    public void testConcurrentAccess()
        throws Exception
    {
    	Worker[] worker = new Worker[THREADS];

        for (int i = 0; i < THREADS; i++)
        {
        	worker[i] = new Worker();
        	worker[i].start();
        }

        for (int i = 0; i < THREADS; i++)
        {
        	worker[i].join();
        }

        assertEquals("Error count should be 0",  0, errcount.intValue());
    }

}

@@ -835,16 +835,97 @@
 e.%0A */%0A%0A
+import java.util.ArrayList;%0Aimport java.util.Collections;%0Aimport java.util.List;%0A
 import j
@@ -966,16 +966,16 @@
 nteger;%0A
-
 %0Aimport 
@@ -1644,16 +1644,115 @@
 count;%0A%0A
+    /**%0A     * Collect all value mismatches%0A     */%0A    protected List%3CString%3E valueMismatchList;%0A%0A
     @Ove
@@ -1971,16 +1971,99 @@
 ger(0);%0A
+        valueMismatchList = Collections.synchronizedList(new ArrayList%3CString%3E());%0A
 %09%7D%0A%0A    
@@ -3665,28 +3665,125 @@
         
-assertEquals
+if (!String.valueOf(idx-1).equals(res))%0A%09%09            %7B%0A%09%09                valueMismatchList.add(String.format
 (%22Values
@@ -3795,16 +3795,25 @@
 ot match
+: %25s - %25s
 %22, Strin
@@ -3834,18 +3834,35 @@
 1), res)
+)
 ;%0A
+%09%09            %7D%0A
 %09%09%09%09%7D%0A%0A%09
@@ -4572,32 +4572,32 @@
 n();%0A        %7D%0A%0A
-
         assertEq
@@ -4654,16 +4654,206 @@
 lue());%0A
+        for (String msg : valueMismatchList)%0A        %7B%0A            System.out.println(msg);%0A        %7D%0A        assertEquals(%22Value mismatch count should be 0%22,  0, valueMismatchList.size());%0A
     %7D%0A%0A%7D

Fix assert in thread