/** * Licensed to JumpMind Inc under one or more contributor * license agreements. See the NOTICE file distributed * with this work for additional information regarding * copyright ownership. JumpMind Inc licenses this file * to you under the GNU General Public License, version 3.0 (GPLv3) * (the "License"); you may not use this file except in compliance * with the License. * * You should have received a copy of the GNU General Public License, * version 3.0 (GPLv3) along with this library; if not, see * <http://www.gnu.org/licenses/>. * * 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.jumpmind.symmetric.test; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.jumpmind.db.DbTestUtils; import org.jumpmind.db.model.Database; import org.jumpmind.db.model.Table; import org.jumpmind.db.platform.IDatabasePlatform; import org.jumpmind.exception.InterruptedException; import org.jumpmind.exception.IoException; import org.jumpmind.properties.EnvironmentSpecificProperties; import org.jumpmind.properties.TypedProperties; import org.jumpmind.symmetric.ClientSymmetricEngine; import org.jumpmind.symmetric.ISymmetricEngine; import org.jumpmind.symmetric.SymmetricWebServer; import org.jumpmind.symmetric.common.ParameterConstants; import org.jumpmind.symmetric.common.SystemConstants; import org.jumpmind.symmetric.model.IncomingBatch; import org.jumpmind.symmetric.model.IncomingBatch.Status; import org.jumpmind.symmetric.model.RemoteNodeStatuses; import org.jumpmind.symmetric.service.IDataLoaderService; import org.jumpmind.util.AppUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; abstract public class AbstractTest { protected final Logger log = LoggerFactory.getLogger(getClass()); private Map<String, SymmetricWebServer> webServers = new HashMap<String, SymmetricWebServer>(); private static final String DEFAULT_PORT = "9995"; private int registrationPort; private int port; static { System.setProperty("h2.baseDir", "./"); } /** * The registration server should always be the first group in the list */ protected String[] getGroupNames() { return new String[] { "root", "client" }; } protected SymmetricWebServer getRegServer() { return getWebServer(getGroupNames()[0]); } protected Table[] getTables(String name) { return null; } protected Properties getProperties(String name) { TypedProperties properties = new TypedProperties(getClass().getResourceAsStream( "/symmetric-test.properties")); properties.setProperty(ParameterConstants.ENGINE_NAME, name); properties.setProperty(ParameterConstants.AUTO_INSERT_REG_SVR_IF_NOT_FOUND, "true"); properties.setProperty(ParameterConstants.EXTERNAL_ID, name); properties.setProperty(ParameterConstants.NODE_GROUP_ID, name); properties.setProperty(ParameterConstants.SYNC_URL, "http://localhost:" + port + "/sync/" + name); properties.setProperty(ParameterConstants.REGISTRATION_URL, "http://localhost:" + registrationPort + "/sync/" + getGroupNames()[0]); return properties; } @Before public void setup() { port = Integer.parseInt(System.getProperty(AppUtils.SYSPROP_PORT_NUMBER, DEFAULT_PORT)); registrationPort = Integer.parseInt(System.getProperty(AppUtils.SYSPROP_PORT_NUMBER, DEFAULT_PORT)); log.info("Running " + getClass().getSimpleName() + " test on port " + port); TestSetupUtil.removeEmbededdedDatabases(); String[] groups = getGroupNames(); for (String group : groups) { getWebServer(group); } } @Test(timeout = 240000) public void test() throws Exception { ISymmetricEngine rootServer = getRegServer().getEngine(); ISymmetricEngine clientServer = getWebServer("client").getEngine(); test(rootServer, clientServer); } protected abstract void test(ISymmetricEngine rootServer, ISymmetricEngine clientServer) throws Exception; @After public void teardown() { String[] groups = getGroupNames(); for (String group : groups) { SymmetricWebServer webServer = getWebServer(group); if (webServer != null) { try { webServer.stop(); } catch (Exception e) { log.error("", e); } } } } protected SymmetricWebServer getWebServer(String name) { try { if (!webServers.containsKey(name)) { EnvironmentSpecificProperties properties = new EnvironmentSpecificProperties( new URL[] { getResource(DbTestUtils.DB_TEST_PROPERTIES) }, "test." + name, new String[] { name }); properties.putAll(getProperties(name)); File rootDir = new File("target/" + name); FileUtils.deleteDirectory(rootDir); rootDir.mkdirs(); File engineDir = new File(rootDir, "engines"); engineDir.mkdirs(); File rootPropertiesFile = new File(engineDir, "root.properties"); FileOutputStream fos = new FileOutputStream(rootPropertiesFile); properties.store(fos, "unit tests"); fos.close(); System.setProperty(SystemConstants.SYSPROP_WAIT_FOR_DATABASE, "false"); System.setProperty(SystemConstants.SYSPROP_ENGINES_DIR, engineDir.getAbsolutePath()); System.setProperty(SystemConstants.SYSPROP_WEB_DIR, "src/main/deploy/web"); ISymmetricEngine engine = null; int tries = 2; do { /** * Firebird is flaky. Trying to work around it. */ try { engine = new ClientSymmetricEngine(properties); } catch (Exception ex) { log.warn("Failed to create engine on the first try. Trying again. The root cause of the first failure was: ", ex); tries--; AppUtils.sleep(30000); } } while (tries > 0 && engine == null); IDatabasePlatform platform = engine.getDatabasePlatform(); engine.getStagingManager().clean(0); engine.uninstall(); Database database = platform.getDdlReader().readTables( platform.getDefaultCatalog(), platform.getDefaultSchema(), new String[] { "TABLE" }); platform.dropDatabase(database, true); Table[] tables = getTables(name); if (tables != null) { platform.alterCaseToMatchDatabaseDefaultCase(tables); platform.createTables(false, true, tables); } engine.destroy(); SymmetricWebServer server = new SymmetricWebServer(); server.setJmxEnabled(false); server.setHttpPort(port); log.info("Starting " + name + " on port " + port); server.setJoin(false); server.start(); server.waitForEnginesToComeOnline(240000); webServers.put(name, server); port += 200; } return webServers.get(name); } catch (IOException e) { throw new IoException(e); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } protected URL getResource(String resource) { return AbstractTest.class.getResource(resource); } /** * Loads configuration in the format of classname.csv at the registration * server */ protected void loadConfigAtRegistrationServer() throws Exception { ISymmetricEngine regEngine = getRegServer().getEngine(); IDataLoaderService dataLoaderService = regEngine.getDataLoaderService(); boolean inError = false; String fileName = getClass().getSimpleName() + ".csv"; log.info("Loading " + fileName + " on " + regEngine.getEngineName()); InputStream is = getClass().getResourceAsStream(fileName); assertNotNull("Could not find configuration as a resource", is); List<IncomingBatch> batches = dataLoaderService.loadDataBatch(IOUtils.toString(is)); for (IncomingBatch batch : batches) { if (batch.getStatus() == Status.ER) { inError = true; } } assertFalse("Failed to load configuration", inError); } protected boolean pull(String name) { int tries = 0; boolean pulled = false; boolean lastPull = false; boolean errorOccurred = false; while (!errorOccurred && (lastPull || (!pulled && tries < 10))) { RemoteNodeStatuses statuses = getWebServer(name).getEngine().pull(); try { statuses.waitForComplete(60000); } catch (InterruptedException ex) { log.warn(ex.getMessage()); } lastPull = statuses.wasDataProcessed(); errorOccurred = statuses.errorOccurred(); pulled |= lastPull; AppUtils.sleep(100); tries++; } return pulled; } protected boolean push(String name) { getWebServer(name).getEngine().route(); int tries = 0; boolean push = false; boolean lastPush = false; boolean errorOccurred = false; while (!errorOccurred && (lastPush || (!push && tries < 10))) { RemoteNodeStatuses statuses = getWebServer(name).getEngine().push(); try { statuses.waitForComplete(60000); } catch (InterruptedException ex) { log.warn(ex.getMessage()); } lastPush = statuses.wasDataProcessed(); errorOccurred = statuses.errorOccurred(); push |= lastPush; AppUtils.sleep(100); tries++; } return push; } protected boolean pullFiles(String name) { int tries = 0; boolean pulled = false; boolean lastPull = false; boolean errorOccurred = false; while (!errorOccurred && (lastPull || (!pulled && tries < 10))) { RemoteNodeStatuses statuses = getWebServer(name).getEngine().getFileSyncService() .pullFilesFromNodes(true); try { statuses.waitForComplete(60000); } catch (InterruptedException ex) { log.warn(ex.getMessage()); } lastPull = statuses.wasDataProcessed(); errorOccurred = statuses.errorOccurred(); pulled |= lastPull; AppUtils.sleep(100); tries++; } return pulled; } protected boolean pushFiles(String name) { int tries = 0; boolean pulled = false; while (!pulled && tries < 10) { RemoteNodeStatuses statuses = getWebServer(name).getEngine().getFileSyncService() .pushFilesToNodes(true); try { statuses.waitForComplete(60000); } catch (InterruptedException ex) { log.warn(ex.getMessage()); } pulled = statuses.wasDataProcessed(); AppUtils.sleep(100); tries++; } return pulled; } protected void loadConfigAndRegisterNode(String clientGroup, String serverGroup) throws Exception { loadConfigAtRegistrationServer(); getWebServer(serverGroup).getEngine().getFileSyncService().trackChanges(true); getWebServer(serverGroup).getEngine().route(); getWebServer(serverGroup).getEngine().openRegistration(clientGroup, clientGroup); pull(clientGroup); } }