001/*
002 * (C) Copyright 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 *     Thierry Delprat <tdelprat@nuxeo.com>
018 */
019package org.nuxeo.elasticsearch.seqgen;
020
021import java.util.NoSuchElementException;
022
023import org.elasticsearch.action.index.IndexRequest;
024import org.elasticsearch.action.index.IndexResponse;
025import org.elasticsearch.common.xcontent.XContentType;
026import org.elasticsearch.index.VersionType;
027import org.nuxeo.ecm.core.api.NuxeoException;
028import org.nuxeo.ecm.core.uidgen.AbstractUIDSequencer;
029import org.nuxeo.ecm.core.uidgen.UIDSequencer;
030import org.nuxeo.elasticsearch.ElasticSearchConstants;
031import org.nuxeo.elasticsearch.api.ESClient;
032import org.nuxeo.elasticsearch.api.ElasticSearchAdmin;
033import org.nuxeo.runtime.api.Framework;
034
035/**
036 * Elasticsearch implementation of {@link UIDSequencer}.
037 * <p>
038 * Since elasticsearch does not seem to support a notion of native sequence, the implementation uses the auto-increment
039 * of the version attribute as described in the <a href=
040 * "http://blogs.perl.org/users/clinton_gormley/2011/10/elasticsearchsequence---a-blazing-fast-ticket-server.html"
041 * >ElasticSearch::Sequence - a blazing fast ticket server</a> blog post.
042 *
043 * @since 7.3
044 */
045public class ESUIDSequencer extends AbstractUIDSequencer {
046
047    protected ESClient esClient = null;
048
049    protected String indexName;
050
051    @Override
052    public void init() {
053        if (esClient != null) {
054            return;
055        }
056        ElasticSearchAdmin esa = Framework.getService(ElasticSearchAdmin.class);
057        esClient = esa.getClient();
058        indexName = esa.getIndexNameForType(ElasticSearchConstants.SEQ_ID_TYPE);
059        try {
060            boolean indexExists = esClient.indexExists(indexName);
061            if (!indexExists) {
062                throw new NuxeoException(
063                        String.format("Sequencer %s needs an elasticSearchIndex contribution with type %s", getName(),
064                                ElasticSearchConstants.SEQ_ID_TYPE));
065            }
066        } catch (NoSuchElementException | NuxeoException e) {
067            dispose();
068            throw e;
069        }
070    }
071
072    @Override
073    public void dispose() {
074        if (esClient == null) {
075            return;
076        }
077        esClient = null;
078        indexName = null;
079    }
080
081    @Override
082    public void initSequence(String key, long id) {
083        String source = "{ \"ts\" : " + System.currentTimeMillis() + "}";
084        IndexResponse res = esClient.index(
085                new IndexRequest(indexName, ElasticSearchConstants.SEQ_ID_TYPE, key).versionType(VersionType.EXTERNAL)
086                        .version(id)
087                        .source(source, XContentType.JSON));
088    }
089
090    @Override
091    public long getNextLong(String sequenceName) {
092        String source = "{ \"ts\" : " + System.currentTimeMillis() + "}";
093        IndexResponse res = esClient.index(
094                new IndexRequest(indexName, ElasticSearchConstants.SEQ_ID_TYPE, sequenceName)
095                        .source(source, XContentType.JSON));
096        return res.getVersion();
097    }
098
099}