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    private static final String DEFAULT_MONGODB_SERVER = "localhost:27017";
080
081    private 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 = "false";
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    public StorageConfiguration(CoreFeature feature) {
102        coreType = defaultSystemProperty(CORE_PROPERTY, DEFAULT_CORE);
103        this.feature = feature;
104    }
105
106    protected static String defaultSystemProperty(String name, String def) {
107        String value = System.getProperty(name);
108        if (value == null || value.equals("") || value.equals("${" + name + "}")) {
109            System.setProperty(name, value = def);
110        }
111        return value;
112    }
113
114    protected static String defaultProperty(String name, String def) {
115        String value = System.getProperty(name);
116        if (value == null || value.equals("") || value.equals("${" + name + "}")) {
117            value = def;
118        }
119        Framework.getProperties().setProperty(name, value);
120        return value;
121    }
122
123    protected void init() {
124        changeTokenEnabled = Boolean.parseBoolean(
125                defaultProperty(CHANGE_TOKEN_ENABLED_PROPERTY, CHANGE_TOKEN_ENABLED_DEFAULT));
126        initJDBC();
127        switch (coreType) {
128        case CORE_VCS:
129            isVCS = true;
130            break;
131        case CORE_MEM:
132            isDBS = true;
133            break;
134        case CORE_MONGODB:
135            isDBS = true;
136            initMongoDB();
137            break;
138        default:
139            isDBS = true;
140            initExternal();
141        }
142    }
143
144    protected void initJDBC() {
145        databaseHelper = DatabaseHelper.DATABASE;
146
147        String msg = "Deploying JDBC using " + databaseHelper.getClass().getSimpleName();
148        // System.out used on purpose, don't remove
149        System.out.println(getClass().getSimpleName() + ": " + msg);
150        log.info(msg);
151
152        // setup system properties for generic XML extension points
153        // this is used both for VCS (org.nuxeo.ecm.core.storage.sql.RepositoryService)
154        // and DataSources (org.nuxeo.runtime.datasource) extension points
155        try {
156            databaseHelper.setUp();
157        } catch (SQLException e) {
158            throw new NuxeoException(e);
159        }
160    }
161
162    protected void initMongoDB() {
163        String server = defaultProperty(MONGODB_SERVER_PROPERTY, DEFAULT_MONGODB_SERVER);
164        String dbname = defaultProperty(MONGODB_DBNAME_PROPERTY, DEFAULT_MONGODB_DBNAME);
165        MongoDBRepositoryDescriptor descriptor = new MongoDBRepositoryDescriptor();
166        descriptor.name = getRepositoryName();
167        descriptor.server = server;
168        descriptor.dbname = dbname;
169        try {
170            clearMongoDB(descriptor);
171        } catch (UnknownHostException e) {
172            throw new NuxeoException(e);
173        }
174    }
175
176    protected void clearMongoDB(MongoDBRepositoryDescriptor descriptor) throws UnknownHostException {
177        MongoClient mongoClient = MongoDBRepository.newMongoClient(descriptor);
178        try {
179            DBCollection coll = MongoDBRepository.getCollection(descriptor, mongoClient);
180            coll.dropIndexes();
181            coll.remove(new BasicDBObject());
182            coll = MongoDBRepository.getCountersCollection(descriptor, mongoClient);
183            coll.dropIndexes();
184            coll.remove(new BasicDBObject());
185        } finally {
186            mongoClient.close();
187        }
188    }
189
190    protected void initExternal() {
191        // Get DBSHelper by reflection
192        String className = String.format("org.nuxeo.ecm.core.storage.%s.DBSHelperImpl", coreType);
193        try {
194            dbsHelper = (DBSHelper) Class.forName(className).newInstance();
195            dbsHelper.init();
196        } catch (ReflectiveOperationException e) {
197            throw new NuxeoException("DBSHelperImpl not found: " + className, e);
198        }
199    }
200
201    public boolean isVCS() {
202        return isVCS;
203    }
204
205    public boolean isVCSH2() {
206        return isVCS && databaseHelper instanceof DatabaseH2;
207    }
208
209    public boolean isVCSDerby() {
210        return isVCS && databaseHelper instanceof DatabaseDerby;
211    }
212
213    public boolean isVCSPostgreSQL() {
214        return isVCS && databaseHelper instanceof DatabasePostgreSQL;
215    }
216
217    public boolean isVCSMySQL() {
218        return isVCS && databaseHelper instanceof DatabaseMySQL;
219    }
220
221    public boolean isVCSOracle() {
222        return isVCS && databaseHelper instanceof DatabaseOracle;
223    }
224
225    public boolean isVCSSQLServer() {
226        return isVCS && databaseHelper instanceof DatabaseSQLServer;
227    }
228
229    public boolean isVCSDB2() {
230        return isVCS && databaseHelper instanceof DatabaseDB2;
231    }
232
233    public boolean isDBS() {
234        return isDBS;
235    }
236
237    public boolean isDBSMem() {
238        return isDBS && CORE_MEM.equals(coreType);
239    }
240
241    public boolean isDBSMongoDB() {
242        return isDBS && CORE_MONGODB.equals(coreType);
243    }
244
245    public boolean isDBSExternal() {
246        return dbsHelper != null;
247    }
248
249    public boolean isDBSMarkLogic() {
250        return isDBS && CORE_MARKLOGIC.equals(coreType);
251    }
252
253    public String getRepositoryName() {
254        return "test";
255    }
256
257    /**
258     * For databases that do asynchronous fulltext indexing, sleep a bit.
259     */
260    public void sleepForFulltext() {
261        if (isVCS()) {
262            databaseHelper.sleepForFulltext();
263        } else {
264            // DBS
265        }
266    }
267
268    /**
269     * Sleep a bit to get to the next millisecond, to have different timestamps.
270     */
271    public void maybeSleepToNextSecond() {
272        try {
273            Thread.sleep(1); // 1 millisecond
274        } catch (InterruptedException e) {
275            Thread.currentThread().interrupt(); // restore interrupted status
276            throw new RuntimeException(e);
277        }
278    }
279
280    public void waitForAsyncCompletion() {
281        feature.waitForAsyncCompletion();
282    }
283
284    public void waitForFulltextIndexing() {
285        waitForAsyncCompletion();
286        sleepForFulltext();
287    }
288
289    /**
290     * Checks if the database supports multiple fulltext indexes.
291     */
292    public boolean supportsMultipleFulltextIndexes() {
293        if (isVCS()) {
294            return databaseHelper.supportsMultipleFulltextIndexes();
295        } else {
296            return false; // DBS
297        }
298    }
299
300    public List<String> getExternalBundles() {
301        if (isDBSExternal()) {
302            return Arrays.asList(String.format("org.nuxeo.ecm.core.storage.%s", coreType),
303                    String.format("org.nuxeo.ecm.core.storage.%s.test", coreType));
304        }
305        return Collections.emptyList();
306    }
307
308    public URL getBlobManagerContrib(FeaturesRunner runner) {
309        String bundleName = "org.nuxeo.ecm.core.test";
310        String contribPath = "OSGI-INF/test-storage-blob-contrib.xml";
311        RuntimeHarness harness = runner.getFeature(RuntimeFeature.class).getHarness();
312        Bundle bundle = harness.getOSGiAdapter().getRegistry().getBundle(bundleName);
313        URL contribURL = bundle.getEntry(contribPath);
314        assertNotNull("deployment contrib " + contribPath + " not found", contribURL);
315        return contribURL;
316    }
317
318    public URL getRepositoryContrib(FeaturesRunner runner) {
319        String msg;
320        if (isVCS()) {
321            msg = "Deploying a VCS repository";
322        } else if (isDBS()) {
323            msg = "Deploying a DBS repository using " + coreType;
324        } else {
325            throw new NuxeoException("Unkown test configuration (not vcs/dbs)");
326        }
327        // System.out used on purpose, don't remove
328        System.out.println(getClass().getSimpleName() + ": " + msg);
329        log.info(msg);
330
331        String contribPath;
332        String bundleName;
333        if (isVCS()) {
334            bundleName = "org.nuxeo.ecm.core.storage.sql.test";
335            contribPath = databaseHelper.getDeploymentContrib();
336        } else {
337            bundleName = "org.nuxeo.ecm.core.test";
338            if (isDBSMem()) {
339                contribPath = "OSGI-INF/test-storage-repo-mem-contrib.xml";
340            } else if (isDBSMongoDB()) {
341                contribPath = "OSGI-INF/test-storage-repo-mongodb-contrib.xml";
342            } else if (isDBSExternal()) {
343                bundleName = String.format("org.nuxeo.ecm.core.storage.%s.test", coreType);
344                contribPath = "OSGI-INF/test-storage-repo-contrib.xml";
345            } else {
346                throw new NuxeoException("Unkown DBS test configuration (not mem/mongodb)");
347            }
348        }
349        RuntimeHarness harness = runner.getFeature(RuntimeFeature.class).getHarness();
350        Bundle bundle = harness.getOSGiAdapter().getRegistry().getBundle(bundleName);
351        URL contribURL = bundle.getEntry(contribPath);
352        assertNotNull("deployment contrib " + contribPath + " not found", contribURL);
353        return contribURL;
354    }
355
356    public boolean isChangeTokenEnabled() {
357        return changeTokenEnabled;
358    }
359
360}