All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.test.org.objectweb.howl.log.LogTest Maven / Gradle / Ivy

There is a newer version: 10.0.0-M3
Show newest version
/*
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2004 Bull S.A.
 * All rights reserved.
 * 
 * Contact: [email protected]
 * 
 * This software is licensed under the BSD license.
 * 
 * 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.
 *     
 * 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.
 * 
 * ------------------------------------------------------------------------------
 * $Id: LogTest.java,v 1.31 2006/04/21 15:03:36 girouxm Exp $
 * ------------------------------------------------------------------------------
 */
package org.objectweb.howl.log;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

import junit.extensions.RepeatedTest;
import junit.framework.Test;
import junit.framework.TestSuite;

public class LogTest extends TestDriver
{

  /**
   * Constructor for LogTest.
   * 
   * @param name
   */
  public LogTest(String name) {
    super(name);
  }
  
  protected void setUp() throws Exception {
    super.setUp();

    log = new Logger(cfg);

  }
  
  protected void tearDown() throws Exception {
    super.tearDown();
  }
  
  public static void main(String[] args) throws Exception {
    junit.textui.TestRunner.run(suite());
  }
  
  public static Test suite() {
    TestSuite suite = new TestSuite(LogTest.class);
    return new RepeatedTest(suite, Integer.getInteger("LogTest.repeatcount", 1).intValue());
  }
  
  public void testGetHighMark() throws Exception {
    try {
      log.lfmgr.getHighMark();
      fail("Expected UnsupportedOperationException");
    } catch (UnsupportedOperationException e) {
      ; // expected this
    }
    this.deleteLogFiles();
    log.open();
    log.lfmgr.getHighMark();
    log.close();
  }
  
  public void testGetHighMark_NewFiles() throws Exception {
    deleteLogFiles();
    try {
      log.lfmgr.getHighMark();
      fail("Expected UnsupportedOperationException");
    } catch (UnsupportedOperationException e) {
      ; // expected this
    }
    
    log.open();
    log.lfmgr.getHighMark();
    log.close();
  }
  
  public void testLoggerSingleThread()
    throws LogException, Exception
  {
    log.open();
    log.setAutoMark(true);
    
    prop.setProperty("msg.count", "10");
    workers = 1;
    runWorkers(LogTestWorker.class);
    // log.close(); called by runWorkers()

  }
  
  public void testLoggerAutomarkTrue()
    throws LogException, Exception
  {
    log.open();
    log.setAutoMark(true);

    runWorkers(LogTestWorker.class);
    // log.close(); called by runWorkers()
  }
  
  public void testLoggerReplay() throws Exception {
    // populate log with some records
    log.open();
    log.setAutoMark(true);
    runWorkers(LogTestWorker.class);
    log.close();
    
    // verify that all records are marked processed
    log.open();
    TestLogReader reader = new TestLogReader();
    reader.run(log);
    log.close();
    assertEquals(getName(), 0L, reader.recordCount);
  }
  
  /**
   * Verifies that replay can begin with a log key that
   * has NOT been forced to the journal.
   * 

puts a record to the journal with sync == false, then * trys to replay from the log key for that record. * @throws Exception */ public void testLoggerReplay_unforcedRecord() throws Exception { log.open(); long key = log.put("".getBytes(), false); TestLogReader reader = new TestLogReader(); log.replay(reader, key); log.close(); if (reader.exception != null) throw reader.exception; } /** * Verifies that replay can begin with a log key that * HAS been forced to the journal. *

puts a record to the journal with sync == false, then * trys to replay from the log key for that record. * @throws Exception */ public void testLoggerReplay_forcedRecord() throws Exception { log.open(); long key = log.put("".getBytes(), true); TestLogReader reader = new TestLogReader(); log.replay(reader, key); log.close(); if (reader.exception != null) throw reader.exception; } public void testMultipleClose() throws Exception { log.open(); log.close(); log.close(); } /** * Verify that replay works with newly created files. * @throws Exception */ public void testLoggerReplay_NewFiles() throws Exception { deleteLogFiles(); log.open(); TestLogReader reader = new TestLogReader(); reader.run(log, 0L); log.close(); assertEquals("unexpected records found in new log files", 0L, reader.recordCount); } /** * Verifies that replay does not return records that have * been marked. * * @throws Exception */ public void testLoggerReplay_MarkedRecords() throws Exception { long key = 0L; int count = 0; deleteLogFiles(); // so we know exactly how many records to expect log.open(); // 1. write two records. for (int i=1; i < 10; ++i) { key = log.put(("Record_" + i).getBytes(), false); ++count; } // 1a. replay the records we have so far. // there is no mark yet, so we should ge all of the records TestLogReader reader = new TestLogReader(); reader.printRecord = false; reader.activeMark = log.getActiveMark(); // NOTE - there is no mark yet reader.run(log); assertEquals(getName() + ": unexpected record count:", count, reader.recordCount); key = log.put("Mark; replay should start here".getBytes(), false); // 2. mark log.mark(key, true); // 3. write another record key = log.put("Record_X".getBytes(), true); // 4. replay should get 2 records, the record we just wrote, and the record at the mark. reader = new TestLogReader(); reader.printRecord = false; // true causes reader to print records reader.activeMark = 0L; // replay all records given to onRecord event reader.run(log); assertEquals(getName() + ": unexpected record count:", 2L, reader.recordCount); // 5. verify we get same two records if log is closed and reopened log.close(); log.open(); reader = new TestLogReader(); reader.printRecord = false; // true causes reader to print records reader.run(log); assertEquals(getName() + ": unexpected record count:", 2L, reader.recordCount); // 6. verify we get only 1 record if we skip the marked record reader = new TestLogReader(); reader.printRecord = false; // true causes reader to print records reader.activeMark = log.getActiveMark(); reader.run(log); assertEquals(getName() + ": unexpected record count:", 1L, reader.recordCount); log.close(); } public void testLogClosedException() throws Exception, LogException { log.open(); log.close(); try { log.setAutoMark(false); fail("expected LogClosedException"); } catch (LogClosedException e) { // this is what we expected so ignore it } } /** * FEATURE 300922 * Verify that LogConfigurationException is thrown if multiple * openings on the log are attempted. * * @throws Exception */ public void testLogConfigurationException_Lock() throws Exception { log.open(); Logger log2 = new Logger(cfg); try { log2.open(); } catch (LogConfigurationException e) { // this is what we expected so ignore it log2.close(); } log.close(); } /** * Verify that an invalid buffer class name throws LogConfigurationException. *

The LogConfigurationException occurs after the log files have been * opened and locked. As a result, it is necessary to call close to * unlock the files. * @throws Exception */ public void testLogConfigurationException_ClassNotFound() throws Exception { cfg.setBufferClassName("org.objectweb.howl.log.noSuchBufferClass"); try { log.open(); log.close(); fail("expected LogConfigurationException"); } catch (LogConfigurationException e) { if (!(e.getCause() instanceof ClassNotFoundException)) throw e; // otherwise this is what we expected so ignore it } // close and unlock the log files log.close(); } /** * Verify that log.open() will throw an exception if the configuration * is changed after a set of log files is created. *

In this test, we change the number of log files. *

PRECONDITION: log files exist from prior test. * @throws Exception */ public void testLogConfigurationException_maxLogFiles() throws Exception { // increase number of log files for current set cfg.setMaxLogFiles(cfg.getMaxLogFiles() + 1); // try to open the log -- we should get an error try { log.open(); log.close(); fail("expected LogConfigurationException"); } catch (LogConfigurationException e) { // this is what we expected so ignore it } log.close(); } /** * Verify that FileNotFoundException is processed * correctly. * *

In order to test FileNotFoundException * it is necessary to devise a pathname that will fail * on all platforms. The technique used here is to * create a file, then use the file as a directory * name in cfg.setLogFileDir. So far, all file * systems reject the attempt to create a file * subordinate to another file, so this technique * seems to be platform neutral. * *

The test fails if FileNotFoundException is * not thrown by the logger. * * @throws Exception */ public void testFileNotFoundException() throws Exception { // create a file (not directory) that will be used as LogFileDir for test File invalidDir = new File(outDir, "invalid"); if (!invalidDir.exists() && !invalidDir.createNewFile()) fail("unable to create 'invalid' directory"); String invalid = invalidDir.getPath(); // set log dir to some invalid value cfg.setLogFileDir(invalid); try { log.open(); fail("expected FileNotFoundException"); } catch (FileNotFoundException e) { // this is what we expected } finally { log.close(); } // one more time to make sure the file locks set via system properties are cleared // FEATURE 300922 try { log.open(); fail("expected FileNotFoundException"); } catch (FileNotFoundException e) { // this is what we expected } finally { log.close(); } } public void testLogConfigurationException_1File() throws Exception { // a single log file is not allowed cfg.setMaxLogFiles(1); try { // open should catch this and get an error log.open(); log.close(); fail("expected LogConfigurationException"); } catch (LogConfigurationException e) { // this is what we expected so ignore it } } public void testLogRecordSizeException() throws Exception { log.open(); // record size == block size is guaranteed to fail byte[] data = new byte[cfg.getBufferSize() * 1024]; // BUG 300957 try { log.put(data,false); fail("expected LogRecordSizeException"); } catch (LogRecordSizeException e) { // this is what we expected so ignore it } log.close(); } public void testInvalidLogKeyException_NegativeKey() throws Exception { TestLogReader tlr = new TestLogReader(); log.open(); // try log key == -1 try { log.replay(tlr, -1L); log.close(); fail("expected InvalidLogKeyException (-1)"); } catch (InvalidLogKeyException e) { // this is what we expected } log.close(); } public void testInvalidLogKeyException_InvalidKey() throws Exception { TestLogReader tlr = new TestLogReader(); log.open(); // try a key that is invalid try { log.replay(tlr, (log.getActiveMark() + 1L)); log.close(); fail("expected InvalidLogKeyException"); } catch (InvalidLogKeyException e) { // this is what we expected } finally { saveStats(); } log.close(); } public void testBlockLogBufferSink() throws Exception { cfg.setBufferClassName("org.objectweb.howl.log.BlockLogBufferSink"); log.open(); log.setAutoMark(true); runWorkers(LogTestWorker.class); // log.close(); called by runWorkers() } final class DataRecords { // make class final to clear findbugs warnings final int count; final String[] sVal; final byte[][] r1; final long[] key; final LogRecord lr; DataRecords(int count) { this.count = count; sVal = new String[count]; r1 = new byte[count][]; key = new long[count]; // initialize test records for (int i=0; i< count; ++i) { sVal[i] = "Record_" + (i+1); r1[i] = sVal[i].getBytes(); } int last = count - 1; lr = new LogRecord(sVal[last].length()+6); } void putAll(boolean forceLastRecord) throws Exception { // populate journal with test records for (int i=0; i< count; ++i) { boolean force = (i == sVal.length - 1) ? forceLastRecord : false ; key[i] = log.put(r1[i], force); } } LogRecord verify(int index) throws Exception { log.get(lr, key[index]); verifyLogRecord(lr, sVal[index], key[index]); return lr; } LogRecord verify(int index, LogRecord lr) throws Exception { verifyLogRecord(lr, sVal[index], key[index]); return lr; } } /** * Verify that Logger.get() method returns requested records. *

We write records to the journal than go through * a series of Logger.get() requests to verify that Logger.get() * works as expected. * *

We also verify that channel position is not affected * by the Logger.get() methods. * * @throws Exception */ public void testGetMethods() throws Exception { DataRecords dr = new DataRecords(5); LogRecord lr = dr.lr; // make sure we are working from the beginning of a new file. deleteLogFiles(); log.open(); // populate journal with test records dr.putAll(true); // remember file position for subsequent validations long pos = log.lfmgr.currentLogFile.channel.position(); lr.setFilterCtrlRecords(true); for (int i=0; i < dr.sVal.length; ++i) { dr.verify(i); } // read backwards for (int i = dr.sVal.length-1; i >= 0; --i) { dr.verify(i); } long posNow = log.lfmgr.currentLogFile.channel.position(); assertEquals("File Position", pos, posNow); // check the Logger.getNext method lr = dr.verify(0); for (int i=1; i < dr.count; ++i) { do { lr = log.getNext(lr); } while (lr.isCTRL()); // skip control records dr.verify(i, lr); } posNow = log.lfmgr.currentLogFile.channel.position(); assertEquals("File Position", pos, posNow); // now read to end of journal int recordCount = 0; while (true) { lr = log.getNext(lr); if (lr.type == LogRecordType.END_OF_LOG) break; ++recordCount; } posNow = log.lfmgr.currentLogFile.channel.position(); assertEquals("File Position", pos, posNow); // read backwards, and write a new record after each read for (int j = 0, i = dr.count-1; i >= 0; --i, ++j) { lr = dr.verify(i); log.put(dr.r1[j], true); } // verify that we now have two sets of records lr = dr.verify(0); for (int i=1; i < dr.count; ++i) { do { lr = log.getNext(lr); } while (lr.isCTRL()); // skip control records dr.verify(i, lr); } // make sure have a second set of records. for (int i=0; i < dr.count; ++i) { do { lr = log.getNext(lr); } while (lr.isCTRL()); // skip control records verifyLogRecord(lr, dr.sVal[i], lr.key); } log.close(); } /** * Verify that Logger.get() method returns a record * that was written with force = false. * * @throws Exception */ public void testGetMethods_UnforcedRecord() throws Exception { DataRecords dr = new DataRecords(1); log.open(); dr.putAll(false); dr.verify(0); log.close(); } /** * Verify that getNext method will detect the logical end of * journal by reading a block with bsn < current block. * * @throws Exception */ public void testGetNextMethod() throws Exception { long keys[] = new long[20]; // array of last 10 keys int kx = 0; // index into keys array byte[][] rec = new byte[2][]; rec[0] = "testGetNextMethod_".getBytes(); // start with clean set of log files cfg.setBufferSize(1); cfg.setMaxBlocksPerFile(5); cfg.setLogFileName("TestGetNext"); log = new Logger(cfg); this.deleteLogFiles(); log.open(); log.setAutoMark(true); // to prevent LogOverflowException // write records until journal wraps around int counter = 1; while(true) { rec[1] = Integer.toHexString(counter++).getBytes(); keys[kx++] = log.put(rec,false); kx %= keys.length; if (log.lfmgr.currentLogFile.rewindCounter > 1) break; } // Logger.get() will force the current buffer so we need // to call it before the writer thread puts a new record. counter = 0; LogRecord lr = log.get(null, keys[kx]); /* * write another record in a separate thread to create * a new high-water mark in the log file. * The record is written with force=false to create the * condition reported in BUG 304982 */ Writer writer = new Writer(); writer.run(); writer.join(); assertNull(getName() + ": Exception thrown in Writer thread", writer.exception); // read records until END_OF_LOG long currentKey = lr.key; while (true) { log.getNext(lr); assertFalse(getName() + ": END_OF_LOG not detected",lr.key < currentKey); currentKey = lr.key; if (lr.type == LogRecordType.END_OF_LOG) break; ++counter; } // we should have read back the number of records i assertEquals(getName() + ": unexpected number of records processed", keys.length, counter); } /** * Helper thread for testGetNextMethod */ private class Writer extends Thread { public Exception exception = null; public void run() { try { log.put("TestGetNextExtra".getBytes(),false); // DO NOT force this block to disk } catch (Exception e) { e.printStackTrace(); exception = e; } } } /** * BUG 303659 - Verify that single instance of log can be * opened and closed multiple times. * @throws Exception */ public void testMultipleOpenClose() throws Exception { log.open(); log.setAutoMark(true); prop.setProperty("msg.count", "1"); workers = 1; runWorkers(LogTestWorker.class); // log.close(); called by runWorkers() // now see what happens when we open the log a second time log.open(); runWorkers(LogTestWorker.class); // log.close(); called by runWorkers() // now see what happens when we open the log a third time log.open(); runWorkers(LogTestWorker.class); // log.close(); called by runWorkers() } /** * Verify that a second attempt to open a log causes * a LogConfigurationException. *

BUG 303907 reported by JOTM is a result of * multiple instances of Logger opening same * set of files. The bug resulted from JOTM * running with HOWL_0_1_7 version. * * Multiple openings are also possible * on non-Windows platforms such as * Linux and OS/X. * * @throws Exception */ public void testMultipleOpen() throws Exception { log.open(); Logger l2 = new Logger(cfg); try { l2.open(); fail("expected LogConfigurationException"); } catch (LogConfigurationException e) { ; // expected result } } /** * BUG: 303659 - Verify that log can shut down even if FlushManager is not * running. *

If there are unforced records waiting to be written * to disk at the time the application closes the log, * and if the FlushManager thread has failed for some * reason, then the application will hang in LogBufferManager.flushAll() * waiting for buffers to be returned to the free pool. *

This test simulates failure of the FlushManager by * seting FlushManager.isClosed = true to prevent it from running. * @throws Exception */ public void simulateFlushManagerFailure(boolean flushPartialBuffers) throws Exception { DataRecords dr = new DataRecords(10); cfg.setFlushPartialBuffers(flushPartialBuffers); log = new Logger(cfg); log.open(); log.setAutoMark(true); // simulate FlushManager interrupt log.bmgr.flushManager.isClosed = true; dr.putAll(false); log.close(); } /** * BUG: 303659 - Make sure log can be closed if FlushManager thread has * stopped running. * @throws Exception */ public void testFlushManagerFailure_FPB_TRUE() throws Exception { simulateFlushManagerFailure(true); } /** * BUG: 303659 - Make sure log can be closed if FlushManager thread has * stopped running. * @throws Exception */ public void testFlushManagerFailure_FPB_FALSE() throws Exception { simulateFlushManagerFailure(false); } /** * Verifies the content of the LogRecord is correct. * @param lr LogRecord to be verified * @param eVal expected value * @param eKey expected record key */ void verifyLogRecord(LogRecord lr, String eVal, long eKey) { byte[][] r2 = lr.getFields(); String rVal = new String(r2[0]); assertEquals("Record Type: " + Long.toHexString(lr.type), 0, lr.type); assertEquals("Record Key: " + Long.toHexString(eKey), eKey, lr.key); assertEquals("Record Data", eVal, rVal); assertEquals("Field Count != 1", 1, r2.length); } public void testLogFileOverflow() throws Exception { this.deleteLogFiles(); int recCount = 1; cfg.setMaxLogFiles(2); cfg.setMaxBlocksPerFile(5); log.open(); // display highmark for each log file byte[][] record = new byte[][] {"record".getBytes(), "1".getBytes()}; // keep reference to first log file long initialKey = log.put(record, false); // write records until switch to second log file LogFile firstLF = log.lfmgr.currentLogFile; do { record[1] = Integer.toString(++recCount).getBytes(); log.put(record, false); } while (firstLF.equals(log.lfmgr.currentLogFile)); // write records until switch to first log file -- should get LogFileOverflowException try { LogFile currentLF = log.lfmgr.currentLogFile; do { record[1] = Integer.toString(++recCount).getBytes(); log.put(record, false); } while(currentLF.equals(log.lfmgr.currentLogFile)); fail("LogFileOverflowException expected"); } catch (LogFileOverflowException e) { // expected } // verify that first record in file has not been overwritten. LogRecord lr = log.get(null, 0L); while (lr.type != LogRecordType.USER) log.getNext(lr); assertEquals("Unexpected log key: ", initialKey, lr.key); byte[][] fields = lr.getFields(); assertTrue("record".equals(new String(fields[0]))); assertTrue("1".equals(new String(fields[1]))); // close the log, reopen and write a record -- should get overflow log.close(); log = new Logger(cfg); log.open(); System.out.println("activeMark: " + Long.toHexString(log.lfmgr.activeMark)); try { record[1] = Integer.toString(++recCount).getBytes(); log.put(record, false); fail("Expected LogFileOverflowException"); } catch (LogFileOverflowException e) { // expected } log.close(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy