001/* 002 * (C) Copyright 2017 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 * Kevin Leturc 018 */ 019package org.nuxeo.mongodb.seqgen; 020 021import org.apache.commons.logging.Log; 022import org.apache.commons.logging.LogFactory; 023import org.bson.Document; 024import org.bson.conversions.Bson; 025import org.nuxeo.ecm.core.uidgen.AbstractUIDSequencer; 026import org.nuxeo.ecm.core.uidgen.UIDSequencer; 027import org.nuxeo.mongodb.core.MongoDBConnectionService; 028import org.nuxeo.mongodb.core.MongoDBSerializationHelper; 029import org.nuxeo.runtime.api.Framework; 030import org.nuxeo.runtime.services.config.ConfigurationService; 031 032import com.mongodb.MongoWriteException; 033import com.mongodb.client.MongoCollection; 034import com.mongodb.client.MongoDatabase; 035import com.mongodb.client.model.Filters; 036import com.mongodb.client.model.FindOneAndUpdateOptions; 037import com.mongodb.client.model.ReturnDocument; 038import com.mongodb.client.model.Updates; 039 040/** 041 * MongoDB implementation of {@link UIDSequencer}. 042 * <p> 043 * We use MongoDB upsert feature to provide a sequencer. 044 * 045 * @since 9.1 046 */ 047public class MongoDBUIDSequencer extends AbstractUIDSequencer { 048 049 public static final String SEQUENCE_DATABASE_ID = "sequence"; 050 051 public static final String COLLECTION_NAME_PROPERTY = "nuxeo.mongodb.seqgen.collection.name"; 052 053 public static final String DEFAULT_COLLECTION_NAME = "sequence"; 054 055 public static final Long ONE = Long.valueOf(1L); 056 057 public static final String SEQUENCE_VALUE_FIELD = "sequence"; 058 059 private static final Log log = LogFactory.getLog(MongoDBUIDSequencer.class); 060 061 private MongoCollection<Document> coll; 062 063 @Override 064 public void init() { 065 getCollection(); 066 067 } 068 069 private MongoCollection<Document> getCollection() { 070 if (coll == null) { 071 // Get collection name 072 ConfigurationService configurationService = Framework.getService(ConfigurationService.class); 073 String collName = configurationService.getProperty(COLLECTION_NAME_PROPERTY, DEFAULT_COLLECTION_NAME); 074 // Get a connection to MongoDB 075 MongoDBConnectionService mongoService = Framework.getService(MongoDBConnectionService.class); 076 // Get database 077 MongoDatabase database = mongoService.getDatabase(SEQUENCE_DATABASE_ID); 078 // Get collection 079 coll = database.getCollection(collName); 080 } 081 return coll; 082 } 083 084 @Override 085 public int getNext(String key) { 086 return (int) getNextLong(key); 087 } 088 089 @Override 090 public long getNextLong(String key) { 091 FindOneAndUpdateOptions options = new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER); 092 Bson filter = Filters.eq(MongoDBSerializationHelper.MONGODB_ID, key); 093 Bson update = Updates.inc(SEQUENCE_VALUE_FIELD, ONE); 094 Document sequence = getCollection().findOneAndUpdate(filter, update, options); 095 // If sequence is null, we need to create it 096 if (sequence == null) { 097 try { 098 sequence = new Document(); 099 sequence.put(MongoDBSerializationHelper.MONGODB_ID, key); 100 sequence.put(SEQUENCE_VALUE_FIELD, ONE); 101 getCollection().insertOne(sequence); 102 } catch (MongoWriteException e) { 103 // There was a race condition - just re-run getNextLong 104 if (log.isTraceEnabled()) { 105 log.trace("There was a race condition during '" + key + "' sequence insertion", e); 106 } 107 return getNextLong(key); 108 } 109 } 110 return ((Long) MongoDBSerializationHelper.bsonToFieldMap(sequence).get(SEQUENCE_VALUE_FIELD)).longValue(); 111 } 112 113 @Override 114 public void dispose() { 115 if (coll != null) { 116 coll = null; 117 } 118 } 119 120}