/** * 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 * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * 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.artemis.core.server.files; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.core.paging.PagingStore; import org.apache.activemq.artemis.core.paging.impl.PagingManagerImpl; import org.apache.activemq.artemis.core.paging.impl.PagingStoreFactoryNIO; import org.apache.activemq.artemis.core.persistence.StorageManager; import org.apache.activemq.artemis.core.persistence.impl.nullpm.NullStorageManager; import org.apache.activemq.artemis.core.settings.HierarchicalRepository; import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; import org.apache.activemq.artemis.core.settings.impl.HierarchicalObjectRepository; import org.apache.activemq.artemis.logs.AssertionLoggerHandler; import org.apache.activemq.artemis.utils.OrderedExecutorFactory; import org.apache.activemq.artemis.utils.ThreadLeakCheckRule; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; public class FileMoveManagerTest { @Rule public TemporaryFolder temporaryFolder; @Rule public ThreadLeakCheckRule leakCheckRule = new ThreadLeakCheckRule(); private File dataLocation; private FileMoveManager manager; @Before public void setUp() { dataLocation = new File(temporaryFolder.getRoot(), "data"); dataLocation.mkdirs(); manager = new FileMoveManager(dataLocation, 10); } public FileMoveManagerTest() { File parent = new File("./target/tmp"); parent.mkdirs(); temporaryFolder = new TemporaryFolder(parent); } @Test public void testBackupFiles() { int[] originalFiles = new int[12]; int count = 0; // It will fake folders creation for (int i = 0; i < 12; i++) { originalFiles[count++] = i; File bkp = new File(dataLocation, FileMoveManager.PREFIX + i); bkp.mkdirs(); } Assert.assertEquals(12, manager.getFolders().length); Assert.assertEquals(12, manager.getNumberOfFolders()); assertIDs(originalFiles, manager.getIDlist()); } @Test public void testMinMax() { int[] originalFiles = new int[12]; int count = 0; // It will fake folders creation for (int i = 0; i < 5; i++) { originalFiles[count++] = i; File bkp = new File(dataLocation, FileMoveManager.PREFIX + i); bkp.mkdirs(); } // simulates a hole where someone removed a folder by hand // It will fake folders creation for (int i = 7; i < 14; i++) { originalFiles[count++] = i; File bkp = new File(dataLocation, FileMoveManager.PREFIX + i); bkp.mkdirs(); } Assert.assertEquals(12, manager.getFolders().length); Assert.assertEquals(12, manager.getNumberOfFolders()); int[] ids = manager.getIDlist(); assertIDs(originalFiles, ids); Assert.assertEquals(0, manager.getMinID()); Assert.assertEquals(13, manager.getMaxID()); manager.setMaxFolders(3).checkOldFolders(); Assert.assertEquals(3, manager.getNumberOfFolders()); Assert.assertEquals(13, manager.getMaxID()); Assert.assertEquals(11, manager.getMinID()); } @Test public void testGarbageCreated() { // I'm pretending an admin created a folder here File garbage = new File(dataLocation, "bkp.zzz"); garbage.mkdirs(); testMinMax(); resetTmp(); // the admin renamed a folder maybe garbage = new File(dataLocation, "bkp.001.old"); garbage.mkdirs(); resetTmp(); // the admin renamed a folder maybe garbage = new File(dataLocation, "bkp.1.5"); garbage.mkdirs(); testMinMax(); } @Test public void testNoFolders() { Assert.assertEquals(0, manager.getFolders().length); Assert.assertEquals(0, manager.getNumberOfFolders()); Assert.assertTrue(dataLocation.delete()); Assert.assertEquals(0, manager.getFolders().length); Assert.assertEquals(0, manager.getNumberOfFolders()); } @Test public void testNoFiles() throws Exception { // nothing to be moved, so why to do a backup manager.doMove(); Assert.assertEquals(0, manager.getNumberOfFolders()); } @Test public void testMoveFiles() throws Exception { manager.setMaxFolders(3); for (int bkp = 1; bkp <= 10; bkp++) { for (int i = 0; i < 100; i++) { createFile(dataLocation, i); } manager.doMove(); // We will always have maximum of 3 folders Assert.assertEquals(Math.min(bkp, manager.getMaxFolders()), manager.getNumberOfFolders()); File bkpFolder = manager.getFolder(bkp); FileMoveManager bkp1Manager = new FileMoveManager(bkpFolder, 10); String[] filesAfterMove = bkp1Manager.getFiles(); for (String file : filesAfterMove) { checkFile(bkpFolder, file); } } Assert.assertEquals(manager.getMaxFolders(), manager.getNumberOfFolders()); manager.setMaxFolders(-1).checkOldFolders(); Assert.assertEquals(3, manager.getNumberOfFolders()); manager.setMaxFolders(1).checkOldFolders(); Assert.assertEquals(1, manager.getNumberOfFolders()); Assert.assertEquals(10, manager.getMaxID()); Assert.assertEquals(10, manager.getMinID()); } @Test public void testMoveFolders() throws Exception { manager.setMaxFolders(3); int NUMBER_OF_FOLDERS = 10; int FILES_PER_FOLDER = 10; for (int bkp = 1; bkp <= 10; bkp++) { for (int f = 0; f < NUMBER_OF_FOLDERS; f++) { File folderF = new File(dataLocation, "folder" + f); folderF.mkdirs(); // FILES_PER_FOLDER + f, I'm just creating more files as f grows. // this is just to make each folder unique somehow for (int i = 0; i < FILES_PER_FOLDER + f; i++) { createFile(folderF, i); } } manager.doMove(); // We will always have maximum of 3 folders Assert.assertEquals(Math.min(bkp, manager.getMaxFolders()), manager.getNumberOfFolders()); File bkpFolder = manager.getFolder(bkp); for (int f = 0; f < NUMBER_OF_FOLDERS; f++) { File fileTmp = new File(bkpFolder, "folder" + f); String[] filesOnFolder = fileTmp.list(); Assert.assertEquals(FILES_PER_FOLDER + f, filesOnFolder.length); for (String file : filesOnFolder) { checkFile(fileTmp, file); } } } Assert.assertEquals(manager.getMaxFolders(), manager.getNumberOfFolders()); manager.setMaxFolders(-1).checkOldFolders(); Assert.assertEquals(3, manager.getNumberOfFolders()); manager.setMaxFolders(1).checkOldFolders(); Assert.assertEquals(1, manager.getNumberOfFolders()); Assert.assertEquals(10, manager.getMaxID()); Assert.assertEquals(10, manager.getMinID()); } @Test public void testMaxZero() throws Exception { manager.setMaxFolders(0); int NUMBER_OF_FOLDERS = 10; int FILES_PER_FOLDER = 10; for (int bkp = 1; bkp <= 10; bkp++) { for (int f = 0; f < NUMBER_OF_FOLDERS; f++) { File folderF = new File(dataLocation, "folder" + f); folderF.mkdirs(); // FILES_PER_FOLDER + f, I'm just creating more files as f grows. // this is just to make each folder unique somehow for (int i = 0; i < FILES_PER_FOLDER + f; i++) { createFile(folderF, i); } } manager.doMove(); // We will always have maximum of 3 folders Assert.assertEquals(0, manager.getNumberOfFolders()); } Assert.assertEquals(0, manager.getMaxID()); } @Test public void testMoveOverPaging() throws Exception { AssertionLoggerHandler.startCapture(); ExecutorService threadPool = Executors.newCachedThreadPool(); try { manager.setMaxFolders(3); for (int i = 1; i <= 10; i++) { HierarchicalRepository<AddressSettings> addressSettings = new HierarchicalObjectRepository<>(); AddressSettings settings = new AddressSettings(); settings.setAddressFullMessagePolicy(AddressFullMessagePolicy.PAGE); addressSettings.setDefault(settings); final StorageManager storageManager = new NullStorageManager(); PagingStoreFactoryNIO storeFactory = new PagingStoreFactoryNIO(storageManager, dataLocation, 100, null, new OrderedExecutorFactory(threadPool), true, null); PagingManagerImpl managerImpl = new PagingManagerImpl(storeFactory, addressSettings, -1); managerImpl.start(); PagingStore store = managerImpl.getPageStore(new SimpleString("simple-test")); store.startPaging(); store.stop(); managerImpl.stop(); manager.doMove(); Assert.assertEquals(Math.min(i, manager.getMaxFolders()), manager.getNumberOfFolders()); } Assert.assertFalse("The loggers are complaining about address.txt", AssertionLoggerHandler.findText("address.txt")); } finally { AssertionLoggerHandler.stopCapture(); threadPool.shutdown(); } } private void assertIDs(int[] originalFiles, int[] ids) { Assert.assertEquals(originalFiles.length, ids.length); for (int i = 0; i < ids.length; i++) { Assert.assertEquals(originalFiles[i], ids[i]); } } private void resetTmp() { temporaryFolder.delete(); temporaryFolder.getRoot().mkdirs(); Assert.assertEquals(0, manager.getNumberOfFolders()); } private void createFile(File folder, int i) throws FileNotFoundException { File dataFile = new File(folder, i + ".jrn"); PrintWriter outData = new PrintWriter(new FileOutputStream(dataFile)); outData.print(i); outData.close(); } private void checkFile(File bkpFolder, String file) throws IOException { File fileRead = new File(bkpFolder, file); InputStreamReader stream = new InputStreamReader(new FileInputStream(fileRead)); BufferedReader reader = new BufferedReader(stream); String valueRead = reader.readLine(); int id = Integer.parseInt(file.substring(0, file.indexOf('.'))); Assert.assertEquals("content of the file wasn't the expected", id, Integer.parseInt(valueRead)); } }