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