All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.saly.elasticsearch.riverstate.ElasticsearchRiverStateManager Maven / Gradle / Ivy

/***********************************************************************************************************************
 *
 * Elasticsearch IMAP River - open source IMAP river for Elasticsearch
 * ==========================================
 *
 * Copyright (C) 2014 by Hendrik Saly (http://saly.de) and others.
 * 
 * Contains (partially) copied code from Jörg Prante's Elasticsearch JDBC river (https://github.com/jprante/elasticsearch-river-jdbc)
 *
 ***********************************************************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 *
 ***********************************************************************************************************************
 *
 * $Id:$
 *
 **********************************************************************************************************************/
package de.saly.elasticsearch.riverstate;

import java.io.IOException;

import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import org.elasticsearch.ElasticsearchTimeoutException;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.ESLoggerFactory;
import org.elasticsearch.common.unit.TimeValue;

public class ElasticsearchRiverStateManager implements RiverStateManager {

    private static final String ERRORS_ID = "errors";
    private static final String FOLDERSTATE_ID = "folderstate";
    private static final String RIVERSTATE_TYPE = "imapriverstate";
    private Client client;
    private String index;
    private final ObjectMapper mapper = new ObjectMapper();
    protected final ESLogger logger = ESLoggerFactory.getLogger(this.getClass().getName());

    public ElasticsearchRiverStateManager client(final Client client) {
        this.client = client;
        return this;
    }

    @Override
    public synchronized RiverState getRiverState(final Folder folder) throws MessagingException {

        try {

            waitForCluster();

            if (client.admin().indices().prepareExists(index()).execute().actionGet().isExists()) {

                final GetResponse response = client
                        .prepareGet(index(), RIVERSTATE_TYPE, FOLDERSTATE_ID + "_" + folder.getURLName().toString().hashCode()).execute()
                        .get();

                if (!response.isSourceEmpty()) {
                    return mapper.readValue(response.getSourceAsString(), new TypeReference() {
                    });

                }
            }
        } catch (final Exception ex) {
            throw new MessagingException("Unable to get river state", ex);
        }

        final RiverState rs = new RiverState();
        rs.setFolderUrl(folder.getURLName().toString());
        // rs.setLastUid(1L);
        rs.setExists(true);
        return rs;

    }

    public String index() {
        return index;
    }

    public ElasticsearchRiverStateManager index(final String index) {
        this.index = index;
        return this;
    }

    @Override
    public void onError(final String errmsg, final Folder folder, final Exception e) {

        logger.error("Folder " + folder.getFullName() + " throws an error:" + errmsg + e, e);

        try {
            client.prepareIndex(index(), RIVERSTATE_TYPE, ERRORS_ID + "_" + folder.getURLName().toString().hashCode())
                    .setSource(mapper.writeValueAsString(new IndexableError(null, folder.getURLName().toString(), errmsg + e))).execute()
                    .actionGet();

        } catch (final Exception ex) {
            logger.error("Unable to log an error because of " + ex + errmsg, e);
        }

    }

    @Override
    public void onError(final String errmsg, final Message msg, final Exception e) {

        try {
            logger.error("Message " + ((MimeMessage) msg).getMessageID() + " throws an error: " + errmsg + e, e);

            client.prepareIndex(index(), RIVERSTATE_TYPE, ERRORS_ID + "_" + ((MimeMessage) msg).getMessageID().hashCode())
                    .setSource(mapper.writeValueAsString(new IndexableError(((MimeMessage) msg).getMessageID(), null, errmsg + e)))
                    .execute().actionGet();

        } catch (final Exception ex) {
            logger.error("Unable to log an error because of " + ex + errmsg, e);
        }
    }

    @Override
    public void setRiverState(final RiverState state) throws MessagingException {

        try {
            logger.debug("set riverstate " + state);

            client.prepareIndex(index(), RIVERSTATE_TYPE, FOLDERSTATE_ID + "_" + state.getFolderUrl().hashCode())
                    .setSource(mapper.writeValueAsString(state)).execute().actionGet();

            logger.debug("set riverstate done");
        } catch (final Exception ex) {
            throw new MessagingException("Unable to set river state", ex);
        }

    }

    private void waitForCluster() throws IOException {
        waitForCluster(ClusterHealthStatus.YELLOW, TimeValue.timeValueSeconds(30));
    }

    private void waitForCluster(final ClusterHealthStatus status, final TimeValue timeout) throws IOException {
        try {
            logger.debug("waiting for cluster state {}", status.name());
            final ClusterHealthResponse healthResponse = client.admin().cluster().prepareHealth().setWaitForStatus(status)
                    .setTimeout(timeout).execute().actionGet();
            if (healthResponse.isTimedOut()) {
                throw new IOException("cluster state is " + healthResponse.getStatus().name() + " and not " + status.name()
                        + ", cowardly refusing to continue with operations");
            } else {
                logger.debug("... cluster state ok");
            }
        } catch (final ElasticsearchTimeoutException e) {
            throw new IOException("timeout, cluster does not respond to health request, cowardly refusing to continue with operations");
        }
    }

    private static class IndexableError {
        private final String errormsg;
        private final String folderurl;
        private final String messageid;

        public IndexableError(final String messageid, final String folderurl, final String errormsg) {
            super();
            this.messageid = messageid;
            this.folderurl = folderurl;
            this.errormsg = errormsg;
        }

        public String getErrormsg() {
            return errormsg;
        }

        public String getFolderurl() {
            return folderurl;
        }

        public String getMessageid() {
            return messageid;
        }

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy