Java tutorial
/* * (C) Copyright 2006-2015 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed 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. * * Contributors: * Florent Guillaume */ package org.nuxeo.ecm.core.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.net.URL; import java.net.UnknownHostException; import java.sql.SQLException; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.List; import java.util.function.BiConsumer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.ecm.core.api.NuxeoException; import org.nuxeo.ecm.core.storage.dbs.DBSHelper; import org.nuxeo.ecm.core.storage.mongodb.MongoDBRepository; import org.nuxeo.ecm.core.storage.mongodb.MongoDBRepositoryDescriptor; import org.nuxeo.ecm.core.storage.sql.DatabaseDB2; import org.nuxeo.ecm.core.storage.sql.DatabaseDerby; import org.nuxeo.ecm.core.storage.sql.DatabaseH2; import org.nuxeo.ecm.core.storage.sql.DatabaseHelper; import org.nuxeo.ecm.core.storage.sql.DatabaseMySQL; import org.nuxeo.ecm.core.storage.sql.DatabaseOracle; import org.nuxeo.ecm.core.storage.sql.DatabasePostgreSQL; import org.nuxeo.ecm.core.storage.sql.DatabaseSQLServer; import org.nuxeo.runtime.api.Framework; import org.nuxeo.runtime.test.runner.FeaturesRunner; import org.nuxeo.runtime.test.runner.RuntimeFeature; import org.nuxeo.runtime.test.runner.RuntimeHarness; import org.osgi.framework.Bundle; import com.mongodb.BasicDBObject; import com.mongodb.DBCollection; import com.mongodb.MongoClient; /** * Description of the specific capabilities of a repository for tests, and helper methods. * * @since 7.3 */ public class StorageConfiguration { private static final Log log = LogFactory.getLog(StorageConfiguration.class); public static final String CORE_PROPERTY = "nuxeo.test.core"; public static final String CORE_VCS = "vcs"; public static final String CORE_MEM = "mem"; public static final String CORE_MONGODB = "mongodb"; public static final String CORE_MARKLOGIC = "marklogic"; public static final String DEFAULT_CORE = CORE_VCS; private static final String MONGODB_SERVER_PROPERTY = "nuxeo.test.mongodb.server"; private static final String MONGODB_DBNAME_PROPERTY = "nuxeo.test.mongodb.dbname"; private static final String DEFAULT_MONGODB_SERVER = "localhost:27017"; private static final String DEFAULT_MONGODB_DBNAME = "unittests"; private String coreType; private boolean isVCS; private boolean isDBS; private DatabaseHelper databaseHelper; private DBSHelper dbsHelper; final CoreFeature feature; public StorageConfiguration(CoreFeature feature) { coreType = defaultSystemProperty(CORE_PROPERTY, DEFAULT_CORE); this.feature = feature; } protected static String defaultSystemProperty(String name, String def) { String value = System.getProperty(name); if (value == null || value.equals("") || value.equals("${" + name + "}")) { System.setProperty(name, value = def); } return value; } protected static String defaultProperty(String name, String def) { String value = System.getProperty(name); if (value == null || value.equals("") || value.equals("${" + name + "}")) { value = def; } Framework.getProperties().setProperty(name, value); return value; } protected void init() { initJDBC(); switch (coreType) { case CORE_VCS: isVCS = true; break; case CORE_MEM: isDBS = true; break; case CORE_MONGODB: isDBS = true; initMongoDB(); break; default: isDBS = true; initExternal(); } } protected void initJDBC() { databaseHelper = DatabaseHelper.DATABASE; String msg = "Deploying JDBC using " + databaseHelper.getClass().getSimpleName(); // System.out used on purpose, don't remove System.out.println(getClass().getSimpleName() + ": " + msg); log.info(msg); // setup system properties for generic XML extension points // this is used both for VCS (org.nuxeo.ecm.core.storage.sql.RepositoryService) // and DataSources (org.nuxeo.runtime.datasource) extension points try { databaseHelper.setUp(); } catch (SQLException e) { throw new NuxeoException(e); } } protected void initMongoDB() { String server = defaultProperty(MONGODB_SERVER_PROPERTY, DEFAULT_MONGODB_SERVER); String dbname = defaultProperty(MONGODB_DBNAME_PROPERTY, DEFAULT_MONGODB_DBNAME); MongoDBRepositoryDescriptor descriptor = new MongoDBRepositoryDescriptor(); descriptor.name = getRepositoryName(); descriptor.server = server; descriptor.dbname = dbname; try { clearMongoDB(descriptor); } catch (UnknownHostException e) { throw new NuxeoException(e); } } protected void clearMongoDB(MongoDBRepositoryDescriptor descriptor) throws UnknownHostException { MongoClient mongoClient = MongoDBRepository.newMongoClient(descriptor); try { DBCollection coll = MongoDBRepository.getCollection(descriptor, mongoClient); coll.dropIndexes(); coll.remove(new BasicDBObject()); coll = MongoDBRepository.getCountersCollection(descriptor, mongoClient); coll.dropIndexes(); coll.remove(new BasicDBObject()); } finally { mongoClient.close(); } } protected void initExternal() { // Get DBSHelper by reflection String className = String.format("org.nuxeo.ecm.core.storage.%s.DBSHelperImpl", coreType); try { dbsHelper = (DBSHelper) Class.forName(className).newInstance(); dbsHelper.init(); } catch (ReflectiveOperationException e) { throw new NuxeoException("DBSHelperImpl not found: " + className, e); } } public boolean isVCS() { return isVCS; } public boolean isVCSH2() { return isVCS && databaseHelper instanceof DatabaseH2; } public boolean isVCSDerby() { return isVCS && databaseHelper instanceof DatabaseDerby; } public boolean isVCSPostgreSQL() { return isVCS && databaseHelper instanceof DatabasePostgreSQL; } public boolean isVCSMySQL() { return isVCS && databaseHelper instanceof DatabaseMySQL; } public boolean isVCSOracle() { return isVCS && databaseHelper instanceof DatabaseOracle; } public boolean isVCSSQLServer() { return isVCS && databaseHelper instanceof DatabaseSQLServer; } public boolean isVCSDB2() { return isVCS && databaseHelper instanceof DatabaseDB2; } public boolean isDBS() { return isDBS; } public boolean isDBSMem() { return isDBS && CORE_MEM.equals(coreType); } public boolean isDBSMongoDB() { return isDBS && CORE_MONGODB.equals(coreType); } public boolean isDBSExternal() { return dbsHelper != null; } public boolean isDBSMarkLogic() { return isDBS && CORE_MARKLOGIC.equals(coreType); } public String getRepositoryName() { return "test"; } /** * For databases that do asynchronous fulltext indexing, sleep a bit. */ public void sleepForFulltext() { if (isVCS()) { databaseHelper.sleepForFulltext(); } else { // DBS } } /** * For databases that don't have sub-second resolution, sleep a bit to get to the next second. */ public void maybeSleepToNextSecond() { if (isVCS()) { databaseHelper.maybeSleepToNextSecond(); } else { // DBS } // sleep 1 ms nevertheless to have different timestamps try { Thread.sleep(1); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // restore interrupted status throw new RuntimeException(e); } } /** * Checks if the database has sub-second resolution. */ public boolean hasSubSecondResolution() { if (isVCS()) { return databaseHelper.hasSubSecondResolution(); } else { return true; // DBS } } public void waitForAsyncCompletion() { feature.waitForAsyncCompletion(); } public void waitForFulltextIndexing() { waitForAsyncCompletion(); sleepForFulltext(); } /** * Checks if the database supports multiple fulltext indexes. */ public boolean supportsMultipleFulltextIndexes() { if (isVCS()) { return databaseHelper.supportsMultipleFulltextIndexes(); } else { return false; // DBS } } public List<String> getExternalBundles() { if (isDBSExternal()) { return Arrays.asList(String.format("org.nuxeo.ecm.core.storage.%s", coreType), String.format("org.nuxeo.ecm.core.storage.%s.test", coreType)); } return Collections.emptyList(); } public URL getBlobManagerContrib(FeaturesRunner runner) { String bundleName = "org.nuxeo.ecm.core.test"; String contribPath = "OSGI-INF/test-storage-blob-contrib.xml"; RuntimeHarness harness = runner.getFeature(RuntimeFeature.class).getHarness(); Bundle bundle = harness.getOSGiAdapter().getRegistry().getBundle(bundleName); URL contribURL = bundle.getEntry(contribPath); assertNotNull("deployment contrib " + contribPath + " not found", contribURL); return contribURL; } public URL getRepositoryContrib(FeaturesRunner runner) { String msg; if (isVCS()) { msg = "Deploying a VCS repository"; } else if (isDBS()) { msg = "Deploying a DBS repository using " + coreType; } else { throw new NuxeoException("Unkown test configuration (not vcs/dbs)"); } // System.out used on purpose, don't remove System.out.println(getClass().getSimpleName() + ": " + msg); log.info(msg); String contribPath; String bundleName; if (isVCS()) { bundleName = "org.nuxeo.ecm.core.storage.sql.test"; contribPath = databaseHelper.getDeploymentContrib(); } else { bundleName = "org.nuxeo.ecm.core.test"; if (isDBSMem()) { contribPath = "OSGI-INF/test-storage-repo-mem-contrib.xml"; } else if (isDBSMongoDB()) { contribPath = "OSGI-INF/test-storage-repo-mongodb-contrib.xml"; } else if (isDBSExternal()) { bundleName = String.format("org.nuxeo.ecm.core.storage.%s.test", coreType); contribPath = "OSGI-INF/test-storage-repo-contrib.xml"; } else { throw new NuxeoException("Unkown DBS test configuration (not mem/mongodb)"); } } RuntimeHarness harness = runner.getFeature(RuntimeFeature.class).getHarness(); Bundle bundle = harness.getOSGiAdapter().getRegistry().getBundle(bundleName); URL contribURL = bundle.getEntry(contribPath); assertNotNull("deployment contrib " + contribPath + " not found", contribURL); return contribURL; } public void assertEqualsTimestamp(Calendar expected, Calendar actual) { assertEquals(convertToStoredCalendar(expected), convertToStoredCalendar(actual)); } public void assertNotEqualsTimestamp(Calendar expected, Calendar actual) { assertNotEquals(convertToStoredCalendar(expected), convertToStoredCalendar(actual)); } /** * Due to some DB restriction this method could fire a false negative. For example 1001ms is before 1002ms but it's * not the case for MySQL (they're equals). */ public void assertBeforeTimestamp(Calendar expected, Calendar actual) { BiConsumer<Calendar, Calendar> assertTrue = (exp, act) -> assertTrue(String.format("expected=%s is not before actual=%s", exp, act), exp.before(act)); assertTrue.accept(convertToStoredCalendar(expected), convertToStoredCalendar(actual)); } public void assertNotBeforeTimestamp(Calendar expected, Calendar actual) { BiConsumer<Calendar, Calendar> assertFalse = (exp, act) -> assertFalse(String.format("expected=%s is before actual=%s", exp, act), exp.before(act)); assertFalse.accept(convertToStoredCalendar(expected), convertToStoredCalendar(actual)); } /** * Due to some DB restriction this method could fire a false negative. For example 1002ms is after 1001ms but it's * not the case for MySQL (they're equals). */ public void assertAfterTimestamp(Calendar expected, Calendar actual) { BiConsumer<Calendar, Calendar> assertTrue = (exp, act) -> assertTrue(String.format("expected=%s is not after actual=%s", exp, act), exp.after(act)); assertTrue.accept(convertToStoredCalendar(expected), convertToStoredCalendar(actual)); } public void assertNotAfterTimestamp(Calendar expected, Calendar actual) { BiConsumer<Calendar, Calendar> assertFalse = (exp, act) -> assertFalse(String.format("expected=%s is after actual=%s", exp, act), exp.after(act)); assertFalse.accept(convertToStoredCalendar(expected), convertToStoredCalendar(actual)); } private Calendar convertToStoredCalendar(Calendar calendar) { if (isVCSMySQL() || isVCSSQLServer()) { Calendar result = (Calendar) calendar.clone(); result.setTimeInMillis(convertToStoredTimestamp(result.getTimeInMillis())); return result; } return calendar; } private long convertToStoredTimestamp(long timestamp) { if (isVCSMySQL()) { return timestamp / 1000 * 1000; } else if (isVCSSQLServer()) { // as datetime in SQL Server are rounded to increments of .000, .003, or .007 seconds // see https://msdn.microsoft.com/en-us/library/aa258277(SQL.80).aspx long milliseconds = timestamp % 10; long newTimestamp = timestamp - milliseconds; if (milliseconds == 9) { newTimestamp += 10; } else if (milliseconds >= 5) { newTimestamp += 7; } else if (milliseconds >= 2) { newTimestamp += 3; } return newTimestamp; } return timestamp; } }