/* Copyright (c) 2005 Ricebridge. All Rights Reserved.
 *
 * This file is available under the terms and conditions of the
 * Ricebridge "Open Source API" policy; Ricebridge grants use of this
 * copyrighted work under the terms of a BSD-style license only. See
 * http://www.opensource.org/licenses/bsd-license.php for more
 * information.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 *  - Redistributions of source code must retain the above copyright
 *  notice, this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above
 *  copyright notice, this list of conditions and the following
 *  disclaimer in the documentation and/or other materials provided
 *  with the distribution.
 *
 *  - Neither the name of the Ricebridge nor the names of its
 *  contributors may be used to endorse or promote products derived
 *  from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.  
 */

package com.ricebridge.xmlman.test;


import com.ricebridge.xmlman.*;
import com.ricebridge.xmlman.in.*;
import com.ricebridge.xmlman.in.test.*;
import com.ricebridge.xmlman.log.*;

import org.jostraca.util.*;

import junit.framework.*;
import junit.textui.*;

import org.xml.sax.InputSource;

import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;

import java.util.*;
import java.io.*;
import java.sql.*;


/** Test cases for {@link XmlManager} thread handling.
 *    <p>The <b><a href="ThreadTest.java.html">Source Code</a></b> of this Java class 
 *    is available under a <a href="http://www.opensource.org/licenses/bsd-license.php">BSD-style license</a>.</p>
 */
public class ThreadTest extends TestCase {

  // test framework
  
  public ThreadTest( String pName ) {
    super( pName );
  }

  public static TestSuite suite() {
    return new TestSuite( ThreadTest.class );
  }

  public static void main( String[] pArgs ) {
    //makeThreadLoadFiles();
    TestRunner.run( suite() );
  }



  // test cases

  static RecordSpec rs             = new RecordSpec( "/t/e", new String[] {"@n","text()","a"} );
  static String     loadfileprefix = "/tmp/xmlman/t";
  static String     savefileprefix = "/tmp/xmlman/st";
  static String     filesuffix     = ".xml";

  static volatile int filehandles = 0;

  public interface Operation {
    public boolean perform( XmlManager pXmlManager, int pIndex, int pCycle ) throws Exception;
  }

  public static class NormalStringListLoad implements Operation {
    public boolean perform( XmlManager pXmlManager, int pIndex, int pCycle ) {
      List data = pXmlManager.load( new File( loadfileprefix+pIndex+filesuffix) );
      ThreadTest.checkLoadStringList( pIndex, data );
      assertEquals( pIndex, pXmlManager.getStats().getTotalRecords() );
      return true;
    }
  }


  public static class BackgroundStringListLoad implements Operation {
    public boolean perform( XmlManager pXmlManager, int pIndex, int pCycle ) {
      List data = pXmlManager.load( new File( loadfileprefix+pIndex+filesuffix) );
      assertEquals( 0, data.size() );
      while( !pXmlManager.isLoadFinished() ) {
        try { Thread.sleep(10); } catch( Exception e ) {}
      }
      ThreadTest.checkLoadStringList( pIndex, data );
      assertEquals( pIndex, pXmlManager.getStats().getTotalRecords() );
      return true;
    }
  }



  public static class NormalStringListSave implements Operation {
    public boolean perform( XmlManager pXmlManager, int pIndex, int pCycle ) throws Exception {
      File file = new File( savefileprefix+pCycle+"-"+pIndex+filesuffix);
      writeFile( file, "" );
      pXmlManager.getXmlSpec().setIndent(false);

      try {
        filehandles++;
        pXmlManager.save( file, ThreadTest.makeStringListData(pIndex) );
      }
      finally {
        filehandles--;
      }
      
      ThreadTest.checkSaveStringList( pIndex, file );
      assertEquals( pIndex, pXmlManager.getStats().getTotalRecords() );
      return true;
    }
  }


  public static class BackgroundStringListSave implements Operation {
    public boolean perform( XmlManager pXmlManager, int pIndex, int pCycle ) throws Exception {
      File file = new File( savefileprefix+pCycle+"-"+pIndex+filesuffix);
      writeFile( file, "" );
      pXmlManager.getXmlSpec().setIndent(false);

      try {
        filehandles++;
        pXmlManager.save( file, ThreadTest.makeStringListData(pIndex) );
        assertEquals( "", readFile( file ) );
        while( !pXmlManager.isSaveFinished() ) {
          try { Thread.sleep(10); } catch( Exception e ) {}
        }
      }
      finally {
        filehandles--;
      }
      
      ThreadTest.checkSaveStringList( pIndex, file );
      assertEquals( pIndex, pXmlManager.getStats().getTotalRecords() );
      return true;
    }
  }
  

  public static void makeThreadLoadFiles() throws Exception {
    new File("/tmp/xmlman").mkdir();

    for( int f = 0; f < 100; f++ ) {
      FileOutputStream fos = new FileOutputStream("/tmp/xmlman/t"+f+".xml");
      PrintWriter      pw  = new PrintWriter(fos);

      pw.println("<?xml version='1.0'?><t>");
      for( int i = 0; i < f; i++ ) {
        pw.println("<e n=\""+f+"-"+i+"\">"+f+"-"+i+"t<a>"+f+"-"+i+"a</a></e>");
      }
      pw.println("</t>");
      pw.close();
    }
  }



  public void xtestNormalLoad() throws Exception {
    XmlManager xmlManager = new XmlManager(rs);
    Operation op = new NormalStringListLoad();
    runTest( 1000, 100, xmlManager, ListUtil.make(op));
  }


  public void xtestBackgroundLoad() throws Exception {
    XmlManager xmlManager = new XmlManager(rs);
    xmlManager.getXmlSpec().setBackground( true );
    xmlManager.getXmlSpec().setProperty("test.delay",""+20);
    xmlManager.getXmlManagerStore().setStringArrayRecordListenerClass( BackgroundTest.DelayedStringArrayRecordListener.class );
    Operation op = new BackgroundStringListLoad();
    runTest( 1000, 100, xmlManager, ListUtil.make(op));
  }


  public void xtestNormalSave() throws Exception {
    XmlManager xmlManager = new XmlManager(rs);
    Operation  op = new NormalStringListSave();
    runTest( 1000, 100, xmlManager, ListUtil.make(op));
  }


  public void testBackgroundSave() throws Exception {
    XmlManager xmlManager = new XmlManager(rs);
    xmlManager.getXmlSpec().setBackground( true );
    xmlManager.getXmlSpec().setProperty("test.delay",""+1000);
    xmlManager.getXmlManagerStore().setStringArrayRecordProviderClass( BackgroundTest.DelayedStringArrayRecordProvider.class );
    Operation  op = new BackgroundStringListSave();
    runTest( 1000, 100, xmlManager, ListUtil.make(op));
  }


  public void runTest( int pCycles, int pFiles, XmlManager pXmlManager, List pOperations ) throws Exception {
    final XmlManager xmlManager = pXmlManager;
    final List       ops        = pOperations;
    final int        numops     = ops.size();
    boolean checkFreeMem = true;
    boolean checkThreads = true;
    boolean checkFileHandles = true;

    try {
      for( int t = 0; t < pCycles; t++ ) {
        System.out.println( t+","+getNumThreads()+","+handleMemory( checkFreeMem ) );
        for( int f = 0; f < pFiles; f++ ) {
          handleThreads( checkThreads );
          handleMemory( checkFreeMem );
          handleFileHandles( checkFileHandles );

          final int tt = t;
          final int ff = f;
          Runnable r = new Runnable() {
              public void run() {
                boolean done = false;
                Operation op = (Operation) ops.get(ff%numops);

                // wait for file handles 
                while( !done ) {
                  try {
                    done = op.perform( xmlManager, ff, tt );
                  }
                  catch( Exception de ) {
                    de.printStackTrace();
                    System.out.println( op+":"+de );
                    try { Thread.sleep(500); } catch( Exception e ) {}
                  }
                }
              }
            };

          boolean start = false;
          while( !start ) {
            try {
              new Thread(r).start();
              start = true;
            }
            catch( OutOfMemoryError oome ) {
              System.out.println( oome );
              System.gc();
              try { Thread.sleep(500); } catch( Exception e ) {}
            }
          }
        }
      }
    }
    catch( Throwable t ) {
      System.out.println( "THREADS:"+getNumThreads() );
      t.printStackTrace();
    }
  }

  
  private int getNumThreads() {
    Thread      t   = Thread.currentThread();
    ThreadGroup tg  = t.getThreadGroup();
    ThreadGroup ptg = null;
    while( null != (ptg = tg.getParent()) ) { tg = ptg; }
    return tg.activeCount();
  }
  
  
  private int handleThreads( boolean pCheckThreads ) {
    int numt = getNumThreads();
    while( pCheckThreads && numt > 6000 ) {
      System.out.println( "WAITING FOR THREADS:"+numt );
      try { Thread.sleep(100); } catch( Exception e ) {} 
      numt = getNumThreads();
    }
    return numt;
  }


  private long handleMemory( boolean pCheckFreeMem ) {
    Runtime r = Runtime.getRuntime();
    long fm  = r.freeMemory();
    while( pCheckFreeMem && fm < 1024*100 ) {
      try { 
        System.out.println( "WAITING FOR MEM:"+fm );
        System.gc();
        Thread.sleep(500); 
        fm = r.freeMemory();
      } catch( Exception e ) {}
    }
    return r.totalMemory()-r.freeMemory();
  }


  private int handleFileHandles( boolean pCheckFileHandles ) {
    int fh = filehandles;
    while( pCheckFileHandles && fh > 128 ) {
      System.out.println( "WAITING FOR FILES:"+fh );
      try { Thread.sleep(100); } catch( Exception e ) {} 
      fh = filehandles;
    }
    return fh;
  }


  public static void checkLoadStringList( int pFileIndex, List pData ) {
    assertEquals( pFileIndex, pData.size() );
    String prefix = pFileIndex+"";
    for( int rI = 0; rI < pData.size(); rI++ ) {
      String[] r = (String[]) pData.get(rI);
      assertTrue( r[0].equals(prefix+"-"+rI) );
      assertTrue( r[1].equals(prefix+"-"+rI+"t") );
      assertTrue( r[2].equals(prefix+"-"+rI+"a") );
    }
  }


  public static void checkSaveStringList( int pFileIndex, File pFile ) throws Exception {
    StringBuffer sb = new StringBuffer("<t>");
    for( int rI = 0; rI < pFileIndex; rI++ ) {
      sb.append("<e n=\""+pFileIndex+"-"+rI+"\">"+pFileIndex+"-"+rI+"t<a>"+pFileIndex+"-"+rI+"a</a></e>");
    }
    sb.append("</t>");
    assertEquals( sb.toString(), readFile( pFile) );
  }

  
  public static List makeStringListData( int pFileIndex ) {
    ArrayList data = new ArrayList( pFileIndex );
    for( int rI = 0; rI < pFileIndex; rI++ ) {
      data.add( new String[] {pFileIndex+"-"+rI, pFileIndex+"-"+rI+"t", pFileIndex+"-"+rI+"a"} );
    }
    return data;
  }

  
  public static void writeFile( File pFile, String pContent ) throws Exception {
    filehandles++;
    FileOutputStream fos = null;
    PrintWriter      pw  = null;
    try {
      fos = new FileOutputStream( pFile );
      pw = new PrintWriter( fos );
      pw.print( pContent );
    }
    catch( Exception e ) {
      throw new Exception( pFile.getAbsolutePath(), e );
    }
    finally {
      pw.close();
      fos.close();
      filehandles--;
    }
  }

  public static String readFile( File pFile ) throws Exception {
    filehandles++;
    FileReader     fr = null;
    BufferedReader br = null;
    try {
      fr = new FileReader( pFile );
      br = new BufferedReader( fr );
      StringBuffer content = new StringBuffer();
      String line = null;
      while( null != (line = br.readLine() ) ) {
        content.append(line);
      }
      return content.toString();
    }
    catch( Exception e ) {
      throw new Exception( pFile.getAbsolutePath(), e );
    }
    finally {
      br.close();
      fr.close();
      filehandles--;
    }
    
  }

}





Syntax Highlighting created using the com.Ostermiller.Syntax package.
Thursday, February 23 2006 at 16:47