001/* 002 * (C) Copyright 2006-2015 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Florent Guillaume 018 */ 019package org.nuxeo.ecm.core.test; 020 021import static org.junit.Assert.assertNotNull; 022 023import java.net.URL; 024import java.net.UnknownHostException; 025import java.sql.SQLException; 026import java.util.Arrays; 027import java.util.Collections; 028import java.util.List; 029 030import org.apache.commons.logging.Log; 031import org.apache.commons.logging.LogFactory; 032import org.nuxeo.ecm.core.api.NuxeoException; 033import org.nuxeo.ecm.core.storage.dbs.DBSHelper; 034import org.nuxeo.ecm.core.storage.mongodb.MongoDBRepository; 035import org.nuxeo.ecm.core.storage.mongodb.MongoDBRepositoryDescriptor; 036import org.nuxeo.ecm.core.storage.sql.DatabaseDB2; 037import org.nuxeo.ecm.core.storage.sql.DatabaseDerby; 038import org.nuxeo.ecm.core.storage.sql.DatabaseH2; 039import org.nuxeo.ecm.core.storage.sql.DatabaseHelper; 040import org.nuxeo.ecm.core.storage.sql.DatabaseMySQL; 041import org.nuxeo.ecm.core.storage.sql.DatabaseOracle; 042import org.nuxeo.ecm.core.storage.sql.DatabasePostgreSQL; 043import org.nuxeo.ecm.core.storage.sql.DatabaseSQLServer; 044import org.nuxeo.runtime.api.Framework; 045import org.nuxeo.runtime.test.runner.FeaturesRunner; 046import org.nuxeo.runtime.test.runner.RuntimeFeature; 047import org.nuxeo.runtime.test.runner.RuntimeHarness; 048import org.osgi.framework.Bundle; 049 050import com.mongodb.BasicDBObject; 051import com.mongodb.DBCollection; 052import com.mongodb.MongoClient; 053 054/** 055 * Description of the specific capabilities of a repository for tests, and helper methods. 056 * 057 * @since 7.3 058 */ 059public class StorageConfiguration { 060 061 private static final Log log = LogFactory.getLog(StorageConfiguration.class); 062 063 public static final String CORE_PROPERTY = "nuxeo.test.core"; 064 065 public static final String CORE_VCS = "vcs"; 066 067 public static final String CORE_MEM = "mem"; 068 069 public static final String CORE_MONGODB = "mongodb"; 070 071 public static final String CORE_MARKLOGIC = "marklogic"; 072 073 public static final String DEFAULT_CORE = CORE_VCS; 074 075 private static final String MONGODB_SERVER_PROPERTY = "nuxeo.test.mongodb.server"; 076 077 private static final String MONGODB_DBNAME_PROPERTY = "nuxeo.test.mongodb.dbname"; 078 079 public static final String DEFAULT_MONGODB_SERVER = "localhost:27017"; 080 081 public static final String DEFAULT_MONGODB_DBNAME = "unittests"; 082 083 private static final String CHANGE_TOKEN_ENABLED_PROPERTY = "nuxeo.test.changetoken.enabled"; 084 085 private static final String CHANGE_TOKEN_ENABLED_DEFAULT = "true"; 086 087 private String coreType; 088 089 private boolean isVCS; 090 091 private boolean isDBS; 092 093 private DatabaseHelper databaseHelper; 094 095 private DBSHelper dbsHelper; 096 097 final CoreFeature feature; 098 099 private boolean changeTokenEnabled; 100 101 protected String mongoDBServer; 102 103 protected String mongoDBDbName; 104 105 public StorageConfiguration(CoreFeature feature) { 106 coreType = defaultSystemProperty(CORE_PROPERTY, DEFAULT_CORE); 107 this.feature = feature; 108 } 109 110 public static String defaultSystemProperty(String name, String def) { 111 String value = System.getProperty(name); 112 if (value == null || value.equals("") || value.equals("${" + name + "}")) { 113 System.setProperty(name, value = def); 114 } 115 return value; 116 } 117 118 protected static String defaultProperty(String name, String def) { 119 String value = System.getProperty(name); 120 if (value == null || value.equals("") || value.equals("${" + name + "}")) { 121 value = def; 122 } 123 Framework.getProperties().setProperty(name, value); 124 return value; 125 } 126 127 protected void init() { 128 changeTokenEnabled = Boolean.parseBoolean( 129 defaultProperty(CHANGE_TOKEN_ENABLED_PROPERTY, CHANGE_TOKEN_ENABLED_DEFAULT)); 130 initJDBC(); 131 switch (coreType) { 132 case CORE_VCS: 133 isVCS = true; 134 break; 135 case CORE_MEM: 136 isDBS = true; 137 break; 138 case CORE_MONGODB: 139 isDBS = true; 140 initMongoDB(); 141 break; 142 default: 143 isDBS = true; 144 initExternal(); 145 } 146 } 147 148 public void initJDBC() { 149 databaseHelper = DatabaseHelper.DATABASE; 150 151 String msg = "Deploying JDBC using " + databaseHelper.getClass().getSimpleName(); 152 // System.out used on purpose, don't remove 153 System.out.println(getClass().getSimpleName() + ": " + msg); 154 log.info(msg); 155 156 // setup system properties for generic XML extension points 157 // this is used both for VCS (org.nuxeo.ecm.core.storage.sql.RepositoryService) 158 // and DataSources (org.nuxeo.runtime.datasource) extension points 159 try { 160 databaseHelper.setUp(); 161 } catch (SQLException e) { 162 throw new NuxeoException(e); 163 } 164 } 165 166 protected void initMongoDB() { 167 mongoDBServer = defaultProperty(MONGODB_SERVER_PROPERTY, DEFAULT_MONGODB_SERVER); 168 mongoDBDbName = defaultProperty(MONGODB_DBNAME_PROPERTY, DEFAULT_MONGODB_DBNAME); 169 MongoDBRepositoryDescriptor descriptor = new MongoDBRepositoryDescriptor(); 170 descriptor.name = getRepositoryName(); 171 descriptor.server = mongoDBServer; 172 descriptor.dbname = mongoDBDbName; 173 try { 174 clearMongoDB(descriptor); 175 } catch (UnknownHostException e) { 176 throw new NuxeoException(e); 177 } 178 } 179 180 protected void clearMongoDB(MongoDBRepositoryDescriptor descriptor) throws UnknownHostException { 181 MongoClient mongoClient = MongoDBRepository.newMongoClient(descriptor); 182 try { 183 DBCollection coll = MongoDBRepository.getCollection(descriptor, mongoClient); 184 coll.dropIndexes(); 185 coll.remove(new BasicDBObject()); 186 coll = MongoDBRepository.getCountersCollection(descriptor, mongoClient); 187 coll.dropIndexes(); 188 coll.remove(new BasicDBObject()); 189 } finally { 190 mongoClient.close(); 191 } 192 } 193 194 protected void initExternal() { 195 // Get DBSHelper by reflection 196 String className = String.format("org.nuxeo.ecm.core.storage.%s.DBSHelperImpl", coreType); 197 try { 198 dbsHelper = (DBSHelper) Class.forName(className).newInstance(); 199 dbsHelper.init(); 200 } catch (ReflectiveOperationException e) { 201 throw new NuxeoException("DBSHelperImpl not found: " + className, e); 202 } 203 } 204 205 public boolean isVCS() { 206 return isVCS; 207 } 208 209 public boolean isVCSH2() { 210 return isVCS && databaseHelper instanceof DatabaseH2; 211 } 212 213 public boolean isVCSDerby() { 214 return isVCS && databaseHelper instanceof DatabaseDerby; 215 } 216 217 public boolean isVCSPostgreSQL() { 218 return isVCS && databaseHelper instanceof DatabasePostgreSQL; 219 } 220 221 public boolean isVCSMySQL() { 222 return isVCS && databaseHelper instanceof DatabaseMySQL; 223 } 224 225 public boolean isVCSOracle() { 226 return isVCS && databaseHelper instanceof DatabaseOracle; 227 } 228 229 public boolean isVCSSQLServer() { 230 return isVCS && databaseHelper instanceof DatabaseSQLServer; 231 } 232 233 public boolean isVCSDB2() { 234 return isVCS && databaseHelper instanceof DatabaseDB2; 235 } 236 237 public boolean isDBS() { 238 return isDBS; 239 } 240 241 public boolean isDBSMem() { 242 return isDBS && CORE_MEM.equals(coreType); 243 } 244 245 public boolean isDBSMongoDB() { 246 return isDBS && CORE_MONGODB.equals(coreType); 247 } 248 249 public boolean isDBSExternal() { 250 return dbsHelper != null; 251 } 252 253 public boolean isDBSMarkLogic() { 254 return isDBS && CORE_MARKLOGIC.equals(coreType); 255 } 256 257 public String getRepositoryName() { 258 return "test"; 259 } 260 261 /** 262 * For databases that do asynchronous fulltext indexing, sleep a bit. 263 */ 264 public void sleepForFulltext() { 265 if (isVCS()) { 266 databaseHelper.sleepForFulltext(); 267 } else { 268 // DBS 269 } 270 } 271 272 /** 273 * Sleep a bit to get to the next millisecond, to have different timestamps. 274 */ 275 public void maybeSleepToNextSecond() { 276 try { 277 Thread.sleep(1); // 1 millisecond 278 } catch (InterruptedException e) { 279 Thread.currentThread().interrupt(); // restore interrupted status 280 throw new RuntimeException(e); 281 } 282 } 283 284 public void waitForAsyncCompletion() { 285 feature.waitForAsyncCompletion(); 286 } 287 288 public void waitForFulltextIndexing() { 289 waitForAsyncCompletion(); 290 sleepForFulltext(); 291 } 292 293 /** 294 * Checks if the database supports multiple fulltext indexes. 295 */ 296 public boolean supportsMultipleFulltextIndexes() { 297 if (isVCS()) { 298 return databaseHelper.supportsMultipleFulltextIndexes(); 299 } else { 300 return false; // DBS 301 } 302 } 303 304 public List<String> getExternalBundles() { 305 if (isDBSExternal()) { 306 return Arrays.asList(String.format("org.nuxeo.ecm.core.storage.%s", coreType), 307 String.format("org.nuxeo.ecm.core.storage.%s.test", coreType)); 308 } 309 return Collections.emptyList(); 310 } 311 312 public URL getBlobManagerContrib(FeaturesRunner runner) { 313 String bundleName = "org.nuxeo.ecm.core.test"; 314 String contribPath = "OSGI-INF/test-storage-blob-contrib.xml"; 315 RuntimeHarness harness = runner.getFeature(RuntimeFeature.class).getHarness(); 316 Bundle bundle = harness.getOSGiAdapter().getRegistry().getBundle(bundleName); 317 URL contribURL = bundle.getEntry(contribPath); 318 assertNotNull("deployment contrib " + contribPath + " not found", contribURL); 319 return contribURL; 320 } 321 322 public URL getRepositoryContrib(FeaturesRunner runner) { 323 String msg; 324 if (isVCS()) { 325 msg = "Deploying a VCS repository"; 326 } else if (isDBS()) { 327 msg = "Deploying a DBS repository using " + coreType; 328 } else { 329 throw new NuxeoException("Unkown test configuration (not vcs/dbs)"); 330 } 331 // System.out used on purpose, don't remove 332 System.out.println(getClass().getSimpleName() + ": " + msg); 333 log.info(msg); 334 335 String contribPath; 336 String bundleName; 337 if (isVCS()) { 338 bundleName = "org.nuxeo.ecm.core.storage.sql.test"; 339 contribPath = databaseHelper.getDeploymentContrib(); 340 } else { 341 bundleName = "org.nuxeo.ecm.core.test"; 342 if (isDBSMem()) { 343 contribPath = "OSGI-INF/test-storage-repo-mem-contrib.xml"; 344 } else if (isDBSMongoDB()) { 345 contribPath = "OSGI-INF/test-storage-repo-mongodb-contrib.xml"; 346 } else if (isDBSExternal()) { 347 bundleName = String.format("org.nuxeo.ecm.core.storage.%s.test", coreType); 348 contribPath = "OSGI-INF/test-storage-repo-contrib.xml"; 349 } else { 350 throw new NuxeoException("Unkown DBS test configuration (not mem/mongodb)"); 351 } 352 } 353 RuntimeHarness harness = runner.getFeature(RuntimeFeature.class).getHarness(); 354 Bundle bundle = harness.getOSGiAdapter().getRegistry().getBundle(bundleName); 355 URL contribURL = bundle.getEntry(contribPath); 356 assertNotNull("deployment contrib " + contribPath + " not found", contribURL); 357 return contribURL; 358 } 359 360 public boolean isChangeTokenEnabled() { 361 return changeTokenEnabled; 362 } 363 364 /** 365 * @since 9.2 366 */ 367 public String getCoreType() { 368 return coreType; 369 } 370 371 /** 372 * @since 9.2 373 */ 374 public String getMongoDBServer() { 375 return mongoDBServer; 376 } 377 378 /** 379 * @since 9.2 380 */ 381 public String getMongoDBDbName() { 382 return mongoDBDbName; 383 } 384 385}