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.sql.SQLException;
025import java.util.Arrays;
026import java.util.Collections;
027import java.util.List;
028
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031import org.nuxeo.ecm.core.api.NuxeoException;
032import org.nuxeo.ecm.core.storage.dbs.DBSHelper;
033import org.nuxeo.ecm.core.storage.sql.DatabaseDB2;
034import org.nuxeo.ecm.core.storage.sql.DatabaseDerby;
035import org.nuxeo.ecm.core.storage.sql.DatabaseH2;
036import org.nuxeo.ecm.core.storage.sql.DatabaseHelper;
037import org.nuxeo.ecm.core.storage.sql.DatabaseMySQL;
038import org.nuxeo.ecm.core.storage.sql.DatabaseOracle;
039import org.nuxeo.ecm.core.storage.sql.DatabasePostgreSQL;
040import org.nuxeo.ecm.core.storage.sql.DatabaseSQLServer;
041import org.nuxeo.runtime.api.Framework;
042import org.nuxeo.runtime.mongodb.MongoDBConnectionHelper;
043import org.nuxeo.runtime.test.runner.FeaturesRunner;
044import org.nuxeo.runtime.test.runner.RuntimeFeature;
045import org.nuxeo.runtime.test.runner.RuntimeHarness;
046import org.osgi.framework.Bundle;
047
048import com.mongodb.MongoClient;
049import com.mongodb.client.MongoDatabase;
050
051/**
052 * Description of the specific capabilities of a repository for tests, and helper methods.
053 *
054 * @since 7.3
055 */
056public class StorageConfiguration {
057
058    private static final Log log = LogFactory.getLog(StorageConfiguration.class);
059
060    public static final String CORE_PROPERTY = "nuxeo.test.core";
061
062    public static final String CORE_VCS = "vcs";
063
064    public static final String CORE_MEM = "mem";
065
066    public static final String CORE_MONGODB = "mongodb";
067
068    public static final String CORE_MARKLOGIC = "marklogic";
069
070    public static final String DEFAULT_CORE = CORE_VCS;
071
072    private static final String MONGODB_SERVER_PROPERTY = "nuxeo.test.mongodb.server";
073
074    private static final String MONGODB_DBNAME_PROPERTY = "nuxeo.test.mongodb.dbname";
075
076    public static final String DEFAULT_MONGODB_SERVER = "localhost:27017";
077
078    public static final String DEFAULT_MONGODB_DBNAME = "unittests";
079
080    private static final String CHANGE_TOKEN_ENABLED_PROPERTY = "nuxeo.test.changetoken.enabled";
081
082    private static final String CHANGE_TOKEN_ENABLED_DEFAULT = "true";
083
084    private String coreType;
085
086    private boolean isVCS;
087
088    private boolean isDBS;
089
090    private DatabaseHelper databaseHelper;
091
092    private DBSHelper dbsHelper;
093
094    final CoreFeature feature;
095
096    private boolean changeTokenEnabled;
097
098    public StorageConfiguration(CoreFeature feature) {
099        coreType = defaultSystemProperty(CORE_PROPERTY, DEFAULT_CORE);
100        this.feature = feature;
101    }
102
103    public static String defaultSystemProperty(String name, String def) {
104        String value = System.getProperty(name);
105        if (value == null || value.equals("") || value.equals("${" + name + "}")) {
106            System.setProperty(name, value = def);
107        }
108        return value;
109    }
110
111    protected static String defaultProperty(String name, String def) {
112        String value = System.getProperty(name);
113        if (value == null || value.equals("") || value.equals("${" + name + "}")) {
114            value = def;
115        }
116        Framework.getProperties().setProperty(name, value);
117        return value;
118    }
119
120    protected void init() {
121        changeTokenEnabled = Boolean.parseBoolean(
122                defaultProperty(CHANGE_TOKEN_ENABLED_PROPERTY, CHANGE_TOKEN_ENABLED_DEFAULT));
123        initJDBC();
124        switch (coreType) {
125        case CORE_VCS:
126            isVCS = true;
127            break;
128        case CORE_MEM:
129            isDBS = true;
130            break;
131        case CORE_MONGODB:
132            isDBS = true;
133            initMongoDB();
134            break;
135        default:
136            isDBS = true;
137            initExternal();
138        }
139    }
140
141    public void initJDBC() {
142        databaseHelper = DatabaseHelper.DATABASE;
143
144        String msg = "Deploying JDBC using " + databaseHelper.getClass().getSimpleName();
145        // System.out used on purpose, don't remove
146        System.out.println(getClass().getSimpleName() + ": " + msg);
147        log.info(msg);
148
149        // setup system properties for generic XML extension points
150        // this is used both for VCS (org.nuxeo.ecm.core.storage.sql.RepositoryService)
151        // and DataSources (org.nuxeo.runtime.datasource) extension points
152        try {
153            databaseHelper.setUp();
154        } catch (SQLException e) {
155            throw new NuxeoException(e);
156        }
157    }
158
159    protected void initMongoDB() {
160        String mongoDBServer = defaultProperty(MONGODB_SERVER_PROPERTY, DEFAULT_MONGODB_SERVER);
161        String mongoDBDbName = defaultProperty(MONGODB_DBNAME_PROPERTY, DEFAULT_MONGODB_DBNAME);
162        try (MongoClient mongoClient = MongoDBConnectionHelper.newMongoClient(mongoDBServer)) {
163            MongoDatabase database = mongoClient.getDatabase(mongoDBDbName);
164            database.drop();
165        }
166    }
167
168    protected void initExternal() {
169        // Get DBSHelper by reflection
170        String className = String.format("org.nuxeo.ecm.core.storage.%s.DBSHelperImpl", coreType);
171        try {
172            dbsHelper = (DBSHelper) Class.forName(className).newInstance();
173            dbsHelper.init();
174        } catch (ReflectiveOperationException e) {
175            throw new NuxeoException("DBSHelperImpl not found: " + className, e);
176        }
177    }
178
179    public boolean isVCS() {
180        return isVCS;
181    }
182
183    public boolean isVCSH2() {
184        return isVCS && databaseHelper instanceof DatabaseH2;
185    }
186
187    public boolean isVCSDerby() {
188        return isVCS && databaseHelper instanceof DatabaseDerby;
189    }
190
191    public boolean isVCSPostgreSQL() {
192        return isVCS && databaseHelper instanceof DatabasePostgreSQL;
193    }
194
195    public boolean isVCSMySQL() {
196        return isVCS && databaseHelper instanceof DatabaseMySQL;
197    }
198
199    public boolean isVCSOracle() {
200        return isVCS && databaseHelper instanceof DatabaseOracle;
201    }
202
203    public boolean isVCSSQLServer() {
204        return isVCS && databaseHelper instanceof DatabaseSQLServer;
205    }
206
207    public boolean isVCSDB2() {
208        return isVCS && databaseHelper instanceof DatabaseDB2;
209    }
210
211    public boolean isDBS() {
212        return isDBS;
213    }
214
215    public boolean isDBSMem() {
216        return isDBS && CORE_MEM.equals(coreType);
217    }
218
219    public boolean isDBSMongoDB() {
220        return isDBS && CORE_MONGODB.equals(coreType);
221    }
222
223    public boolean isDBSExternal() {
224        return dbsHelper != null;
225    }
226
227    public boolean isDBSMarkLogic() {
228        return isDBS && CORE_MARKLOGIC.equals(coreType);
229    }
230
231    public String getRepositoryName() {
232        return "test";
233    }
234
235    /**
236     * For databases that do asynchronous fulltext indexing, sleep a bit.
237     */
238    public void sleepForFulltext() {
239        if (isVCS()) {
240            databaseHelper.sleepForFulltext();
241        } else {
242            // DBS
243        }
244    }
245
246    /**
247     * Sleep a bit to get to the next millisecond, to have different timestamps.
248     */
249    public void maybeSleepToNextSecond() {
250        try {
251            Thread.sleep(1); // 1 millisecond
252        } catch (InterruptedException e) {
253            Thread.currentThread().interrupt(); // restore interrupted status
254            throw new RuntimeException(e);
255        }
256    }
257
258    public void waitForAsyncCompletion() {
259        feature.waitForAsyncCompletion();
260    }
261
262    public void waitForFulltextIndexing() {
263        waitForAsyncCompletion();
264        sleepForFulltext();
265    }
266
267    /**
268     * Checks if the database supports multiple fulltext indexes.
269     */
270    public boolean supportsMultipleFulltextIndexes() {
271        if (isVCS()) {
272            return databaseHelper.supportsMultipleFulltextIndexes();
273        } else {
274            return false; // DBS
275        }
276    }
277
278    public List<String> getExternalBundles() {
279        if (isDBSExternal()) {
280            return Arrays.asList(String.format("org.nuxeo.ecm.core.storage.%s", coreType),
281                                 String.format("org.nuxeo.ecm.core.storage.%s.test", coreType));
282        }
283        return Collections.emptyList();
284    }
285
286    public URL getBlobManagerContrib(FeaturesRunner runner) {
287        String bundleName = "org.nuxeo.ecm.core.test";
288        String contribPath = "OSGI-INF/test-storage-blob-contrib.xml";
289        RuntimeHarness harness = runner.getFeature(RuntimeFeature.class).getHarness();
290        Bundle bundle = harness.getOSGiAdapter().getRegistry().getBundle(bundleName);
291        URL contribURL = bundle.getEntry(contribPath);
292        assertNotNull("deployment contrib " + contribPath + " not found", contribURL);
293        return contribURL;
294    }
295
296    public URL getRepositoryContrib(FeaturesRunner runner) {
297        String msg;
298        if (isVCS()) {
299            msg = "Deploying a VCS repository";
300        } else if (isDBS()) {
301            msg = "Deploying a DBS repository using " + coreType;
302        } else {
303            throw new NuxeoException("Unkown test configuration (not vcs/dbs)");
304        }
305        // System.out used on purpose, don't remove
306        System.out.println(getClass().getSimpleName() + ": " + msg);
307        log.info(msg);
308
309        String contribPath;
310        String bundleName;
311        if (isVCS()) {
312            bundleName = "org.nuxeo.ecm.core.storage.sql.test";
313            contribPath = databaseHelper.getDeploymentContrib();
314        } else {
315            bundleName = "org.nuxeo.ecm.core.test";
316            if (isDBSMem()) {
317                contribPath = "OSGI-INF/test-storage-repo-mem-contrib.xml";
318            } else if (isDBSMongoDB()) {
319                contribPath = "OSGI-INF/test-storage-repo-mongodb-contrib.xml";
320            } else if (isDBSExternal()) {
321                bundleName = String.format("org.nuxeo.ecm.core.storage.%s.test", coreType);
322                contribPath = "OSGI-INF/test-storage-repo-contrib.xml";
323            } else {
324                throw new NuxeoException("Unkown DBS test configuration (not mem/mongodb)");
325            }
326        }
327        RuntimeHarness harness = runner.getFeature(RuntimeFeature.class).getHarness();
328        Bundle bundle = harness.getOSGiAdapter().getRegistry().getBundle(bundleName);
329        URL contribURL = bundle.getEntry(contribPath);
330        assertNotNull("deployment contrib " + contribPath + " not found", contribURL);
331        return contribURL;
332    }
333
334    public boolean isChangeTokenEnabled() {
335        return changeTokenEnabled;
336    }
337
338    /**
339     * @since 9.2
340     */
341    public String getCoreType() {
342        return coreType;
343    }
344
345}