/** * 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. */ package org.apache.activemq.store.kahadb.disk.page; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.HashSet; import org.apache.activemq.store.kahadb.disk.util.StringMarshaller; import junit.framework.TestCase; @SuppressWarnings("rawtypes") public class PageFileTest extends TestCase { public void testCRUD() throws IOException { PageFile pf = new PageFile(new File("target/test-data"), getName()); pf.delete(); pf.load(); HashSet<String> expected = new HashSet<String>(); // Insert some data into the page file. Transaction tx = pf.tx(); for (int i = 0; i < 100; i++) { Page<String> page = tx.allocate(); assertEquals(Page.PAGE_FREE_TYPE, page.getType()); String t = "page:" + i; expected.add(t); page.set(t); tx.store(page, StringMarshaller.INSTANCE, false); tx.commit(); } // Reload it... pf.unload(); pf.load(); tx = pf.tx(); // Iterate it to make sure they are still there.. HashSet<String> actual = new HashSet<String>(); for (Page<String> page : tx) { tx.load(page, StringMarshaller.INSTANCE); actual.add(page.get()); } assertEquals(expected, actual); // Remove the odd records.. for (int i = 0; i < 100; i++) { if (i % 2 == 0) { break; } String t = "page:" + i; expected.remove(t); } for (Page<String> page : tx) { tx.load(page, StringMarshaller.INSTANCE); if (!expected.contains(page.get())) { tx.free(page); } } tx.commit(); // Reload it... pf.unload(); pf.load(); tx = pf.tx(); // Iterate it to make sure the even records are still there.. actual.clear(); for (Page<String> page : tx) { tx.load(page, StringMarshaller.INSTANCE); actual.add((String)page.get()); } assertEquals(expected, actual); // Update the records... HashSet<String> t = expected; expected = new HashSet<String>(); for (String s : t) { expected.add(s + ":updated"); } for (Page<String> page : tx) { tx.load(page, StringMarshaller.INSTANCE); page.set(page.get() + ":updated"); tx.store(page, StringMarshaller.INSTANCE, false); } tx.commit(); // Reload it... pf.unload(); pf.load(); tx = pf.tx(); // Iterate it to make sure the updated records are still there.. actual.clear(); for (Page<String> page : tx) { tx.load(page, StringMarshaller.INSTANCE); actual.add(page.get()); } assertEquals(expected, actual); pf.unload(); } public void testStreams() throws IOException { PageFile pf = new PageFile(new File("target/test-data"), getName()); pf.delete(); pf.load(); Transaction tx = pf.tx(); Page page = tx.allocate(); tx.commit(); OutputStream pos = tx.openOutputStream(page, true); DataOutputStream os = new DataOutputStream(pos); for (int i = 0; i < 10000; i++) { os.writeUTF("Test string:" + i); } os.close(); tx.commit(); // Reload the page file. pf.unload(); pf.load(); tx = pf.tx(); InputStream pis = tx.openInputStream(page); DataInputStream is = new DataInputStream(pis); for (int i = 0; i < 10000; i++) { assertEquals("Test string:" + i, is.readUTF()); } assertEquals(-1, is.read()); is.close(); pf.unload(); } public void testAddRollback() throws IOException { PageFile pf = new PageFile(new File("target/test-data"), getName()); pf.delete(); pf.load(); HashSet<String> expected = new HashSet<String>(); // Insert some data into the page file. Transaction tx = pf.tx(); for (int i = 0; i < 100; i++) { Page<String> page = tx.allocate(); assertEquals(Page.PAGE_FREE_TYPE, page.getType()); String t = "page:" + i; page.set(t); tx.store(page, StringMarshaller.INSTANCE, false); // Rollback every other insert. if (i % 2 == 0) { expected.add(t); tx.commit(); } else { tx.rollback(); } } // Reload it... pf.unload(); pf.load(); tx = pf.tx(); // Iterate it to make sure they are still there.. HashSet<String> actual = new HashSet<String>(); for (Page<String> page : tx) { tx.load(page, StringMarshaller.INSTANCE); actual.add(page.get()); } assertEquals(expected, actual); } //Test for AMQ-6590 public void testFreePageRecoveryUncleanShutdown() throws Exception { PageFile pf = new PageFile(new File("target/test-data"), getName()); pf.delete(); pf.setEnableRecoveryFile(false); pf.load(); //Allocate 10 free pages Transaction tx = pf.tx(); tx.allocate(10); tx.commit(); pf.flush(); //Load a second instance on the same directory fo the page file which //simulates an unclean shutdown from the previous run PageFile pf2 = new PageFile(new File("target/test-data"), getName()); pf2.setEnableRecoveryFile(false); pf2.load(); long freePages = pf2.getFreePageCount(); pf.unload(); pf2.unload(); //Make sure that all 10 pages are still tracked assertEquals(10, freePages); } }