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;
026
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029import org.nuxeo.ecm.core.api.NuxeoException;
030import org.nuxeo.ecm.core.event.EventService;
031import org.nuxeo.ecm.core.storage.mongodb.MongoDBRepository;
032import org.nuxeo.ecm.core.storage.mongodb.MongoDBRepositoryDescriptor;
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.test.runner.FeaturesRunner;
043import org.nuxeo.runtime.test.runner.RuntimeFeature;
044import org.nuxeo.runtime.test.runner.RuntimeHarness;
045import org.osgi.framework.Bundle;
046
047import com.mongodb.BasicDBObject;
048import com.mongodb.DBCollection;
049import com.mongodb.MongoClient;
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 DEFAULT_CORE = CORE_VCS;
069
070    private static final String MONGODB_SERVER_PROPERTY = "nuxeo.test.mongodb.server";
071
072    private static final String MONGODB_DBNAME_PROPERTY = "nuxeo.test.mongodb.dbname";
073
074    private static final String DEFAULT_MONGODB_SERVER = "localhost:27017";
075
076    private static final String DEFAULT_MONGODB_DBNAME = "unittests";
077
078    private String coreType;
079
080    private boolean isVCS;
081
082    private boolean isDBS;
083
084    private DatabaseHelper databaseHelper;
085
086    public StorageConfiguration() {
087        initJDBC();
088        coreType = defaultSystemProperty(CORE_PROPERTY, DEFAULT_CORE);
089        switch (coreType) {
090        case CORE_VCS:
091            isVCS = true;
092            break;
093        case CORE_MEM:
094            isDBS = true;
095            break;
096        case CORE_MONGODB:
097            isDBS = true;
098            initMongoDB();
099            break;
100        default:
101            throw new ExceptionInInitializerError("Unknown test core mode: " + coreType);
102        }
103    }
104
105    protected static String defaultSystemProperty(String name, String def) {
106        String value = System.getProperty(name);
107        if (value == null || value.equals("") || value.equals("${" + name + "}")) {
108            System.setProperty(name, value = def);
109        }
110        return value;
111    }
112
113    protected static String defaultProperty(String name, String def) {
114        String value = System.getProperty(name);
115        if (value == null || value.equals("") || value.equals("${" + name + "}")) {
116            value = def;
117        }
118        Framework.getProperties().setProperty(name, value);
119        return value;
120    }
121
122    protected void initJDBC() {
123        databaseHelper = DatabaseHelper.DATABASE;
124
125        String msg = "Deploying JDBC using " + databaseHelper.getClass().getSimpleName();
126        // System.out used on purpose, don't remove
127        System.out.println(getClass().getSimpleName() + ": " + msg);
128        log.info(msg);
129
130        // setup system properties for generic XML extension points
131        // this is used both for VCS (org.nuxeo.ecm.core.storage.sql.RepositoryService)
132        // and DataSources (org.nuxeo.runtime.datasource) extension points
133        try {
134            databaseHelper.setUp();
135        } catch (SQLException e) {
136            throw new NuxeoException(e);
137        }
138    }
139
140    protected void initMongoDB() {
141        String server = defaultProperty(MONGODB_SERVER_PROPERTY, DEFAULT_MONGODB_SERVER);
142        String dbname = defaultProperty(MONGODB_DBNAME_PROPERTY, DEFAULT_MONGODB_DBNAME);
143        MongoDBRepositoryDescriptor descriptor = new MongoDBRepositoryDescriptor();
144        descriptor.name = getRepositoryName();
145        descriptor.server = server;
146        descriptor.dbname = dbname;
147        try {
148            clearMongoDB(descriptor);
149        } catch (UnknownHostException e) {
150            throw new NuxeoException(e);
151        }
152    }
153
154    protected void clearMongoDB(MongoDBRepositoryDescriptor descriptor) throws UnknownHostException {
155        MongoClient mongoClient = MongoDBRepository.newMongoClient(descriptor);
156        try {
157            DBCollection coll = MongoDBRepository.getCollection(descriptor, mongoClient);
158            coll.dropIndexes();
159            coll.remove(new BasicDBObject());
160            coll = MongoDBRepository.getCountersCollection(descriptor, mongoClient);
161            coll.dropIndexes();
162            coll.remove(new BasicDBObject());
163        } finally {
164            mongoClient.close();
165        }
166    }
167
168    public boolean isVCS() {
169        return isVCS;
170    }
171
172    public boolean isVCSH2() {
173        return isVCS && databaseHelper instanceof DatabaseH2;
174    }
175
176    public boolean isVCSDerby() {
177        return isVCS && databaseHelper instanceof DatabaseDerby;
178    }
179
180    public boolean isVCSPostgreSQL() {
181        return isVCS && databaseHelper instanceof DatabasePostgreSQL;
182    }
183
184    public boolean isVCSMySQL() {
185        return isVCS && databaseHelper instanceof DatabaseMySQL;
186    }
187
188    public boolean isVCSOracle() {
189        return isVCS && databaseHelper instanceof DatabaseOracle;
190    }
191
192    public boolean isVCSSQLServer() {
193        return isVCS && databaseHelper instanceof DatabaseSQLServer;
194    }
195
196    public boolean isVCSDB2() {
197        return isVCS && databaseHelper instanceof DatabaseDB2;
198    }
199
200    public boolean isDBS() {
201        return isDBS;
202    }
203
204    public boolean isDBSMem() {
205        return isDBS && CORE_MEM.equals(coreType);
206    }
207
208    public boolean isDBSMongoDB() {
209        return isDBS && CORE_MONGODB.equals(coreType);
210    }
211
212    public String getRepositoryName() {
213        return "test";
214    }
215
216    /**
217     * For databases that do asynchronous fulltext indexing, sleep a bit.
218     */
219    public void sleepForFulltext() {
220        if (isVCS()) {
221            databaseHelper.sleepForFulltext();
222        } else {
223            // DBS
224        }
225    }
226
227    /**
228     * For databases that don't have sub-second resolution, sleep a bit to get to the next second.
229     */
230    public void maybeSleepToNextSecond() {
231        if (isVCS()) {
232            databaseHelper.maybeSleepToNextSecond();
233        } else {
234            // DBS
235        }
236        // sleep 1 ms nevertheless to have different timestamps
237        try {
238            Thread.sleep(1);
239        } catch (InterruptedException e) {
240            Thread.currentThread().interrupt(); // restore interrupted status
241            throw new RuntimeException(e);
242        }
243    }
244
245    /**
246     * Checks if the database has sub-second resolution.
247     */
248    public boolean hasSubSecondResolution() {
249        if (isVCS()) {
250            return databaseHelper.hasSubSecondResolution();
251        } else {
252            return true; // DBS
253        }
254    }
255
256    public void waitForAsyncCompletion() {
257        Framework.getService(EventService.class).waitForAsyncCompletion();
258    }
259
260    public void waitForFulltextIndexing() {
261        waitForAsyncCompletion();
262        sleepForFulltext();
263    }
264
265    /**
266     * Checks if the database supports multiple fulltext indexes.
267     */
268    public boolean supportsMultipleFulltextIndexes() {
269        if (isVCS()) {
270            return databaseHelper.supportsMultipleFulltextIndexes();
271        } else {
272            return false; // DBS
273        }
274    }
275
276    public URL getBlobManagerContrib(FeaturesRunner runner) {
277        String bundleName = "org.nuxeo.ecm.core.test";
278        String contribPath = "OSGI-INF/test-storage-blob-contrib.xml";
279        RuntimeHarness harness = runner.getFeature(RuntimeFeature.class).getHarness();
280        Bundle bundle = harness.getOSGiAdapter().getRegistry().getBundle(bundleName);
281        URL contribURL = bundle.getEntry(contribPath);
282        assertNotNull("deployment contrib " + contribPath + " not found", contribURL);
283        return contribURL;
284    }
285
286    public URL getRepositoryContrib(FeaturesRunner runner) {
287        String msg;
288        if (isVCS()) {
289            msg = "Deploying a VCS repository";
290        } else if (isDBS()) {
291            msg = "Deploying a DBS repository using " + coreType;
292        } else {
293            throw new NuxeoException("Unkown test configuration (not vcs/dbs)");
294        }
295        // System.out used on purpose, don't remove
296        System.out.println(getClass().getSimpleName() + ": " + msg);
297        log.info(msg);
298
299        String contribPath;
300        String bundleName;
301        if (isVCS()) {
302            bundleName = "org.nuxeo.ecm.core.storage.sql.test";
303            contribPath = databaseHelper.getDeploymentContrib();
304        } else {
305            bundleName = "org.nuxeo.ecm.core.test";
306            if (isDBSMem()) {
307                contribPath = "OSGI-INF/test-storage-repo-mem-contrib.xml";
308            } else if (isDBSMongoDB()) {
309                contribPath = "OSGI-INF/test-storage-repo-mongodb-contrib.xml";
310            } else {
311                throw new NuxeoException("Unkown DBS test configuration (not mem/mongodb)");
312            }
313        }
314        RuntimeHarness harness = runner.getFeature(RuntimeFeature.class).getHarness();
315        Bundle bundle = harness.getOSGiAdapter().getRegistry().getBundle(bundleName);
316        URL contribURL = bundle.getEntry(contribPath);
317        assertNotNull("deployment contrib " + contribPath + " not found", contribURL);
318        return contribURL;
319    }
320
321}