org.waveprotocol.box.server.waveserver.SolrWaveIndexerImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.waveprotocol.box.server.waveserver.SolrWaveIndexerImpl.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

package org.waveprotocol.box.server.waveserver;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListenableFutureTask;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.typesafe.config.Config;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.http.HttpStatus;
import org.waveprotocol.box.common.DeltaSequence;
import org.waveprotocol.box.common.Snippets;
import org.waveprotocol.box.server.executor.ExecutorAnnotations.SolrExecutor;
import org.waveprotocol.box.server.robots.util.ConversationUtil;
import org.waveprotocol.box.stat.Timed;
import org.waveprotocol.wave.model.document.operation.DocInitialization;
import org.waveprotocol.wave.model.id.IdUtil;
import org.waveprotocol.wave.model.id.WaveletName;
import org.waveprotocol.wave.model.version.HashedVersion;
import org.waveprotocol.wave.model.wave.ParticipantId;
import org.waveprotocol.wave.model.wave.data.ReadableBlipData;
import org.waveprotocol.wave.model.wave.data.ReadableWaveletData;
import org.waveprotocol.wave.model.waveref.WaveRef;
import org.waveprotocol.wave.util.escapers.jvm.JavaWaverefEncoder;
import org.waveprotocol.wave.util.logging.Log;

import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.logging.Level;

/**
 * @author Frank R. <renfeng.cn@gmail.com>
 */
@Singleton
public class SolrWaveIndexerImpl extends AbstractWaveIndexer
        implements WaveBus.Subscriber, PerUserWaveViewBus.Listener {

    private static final Log LOG = Log.get(SolrWaveIndexerImpl.class);

    private final Executor executor;
    private final ReadableWaveletDataProvider waveletDataProvider;
    private final String solrBaseUrl;

    @Inject
    public SolrWaveIndexerImpl(WaveMap waveMap, WaveletProvider waveletProvider,
            ReadableWaveletDataProvider waveletDataProvider, ConversationUtil conversationUtil,
            WaveletNotificationDispatcher notificationDispatcher, Config config,
            @SolrExecutor Executor solrExecutor) {
        super(waveMap, waveletProvider);
        executor = solrExecutor;
        solrBaseUrl = config.getString("core.solr_base_url");
        this.waveletDataProvider = waveletDataProvider;
        notificationDispatcher.subscribe(this);
    }

    @Override
    public ListenableFuture<Void> onParticipantAdded(final WaveletName waveletName, ParticipantId participant) {
        /*
         * ignored. See waveletCommitted(WaveletName, HashedVersion)
         */
        return null;
    }

    @Override
    public ListenableFuture<Void> onParticipantRemoved(final WaveletName waveletName, ParticipantId participant) {
        /*
         * ignored. See waveletCommitted(WaveletName, HashedVersion)
         */
        return null;
    }

    @Override
    public ListenableFuture<Void> onWaveInit(final WaveletName waveletName) {

        ListenableFutureTask<Void> task = ListenableFutureTask.create(new Callable<Void>() {

            @Override
            public Void call() throws Exception {
                ReadableWaveletData waveletData;
                try {
                    waveletData = waveletDataProvider.getReadableWaveletData(waveletName);
                    updateIndex(waveletData);
                } catch (WaveServerException e) {
                    LOG.log(Level.SEVERE, "Failed to initialize index for " + waveletName, e);
                    throw e;
                }
                return null;
            }
        });
        executor.execute(task);
        return task;
    }

    @Override
    protected void processWavelet(WaveletName waveletName) {
        onWaveInit(waveletName);
    }

    @Override
    protected void postIndexHook() {
        try {
            getWaveMap().unloadAllWavelets();
        } catch (WaveletStateException e) {
            throw new IndexException("Problem encountered while cleaning up", e);
        }
    }

    @Timed
    private void updateIndex(ReadableWaveletData wavelet) throws IndexException {
        Preconditions.checkNotNull(wavelet);
        if (IdUtil.isConversationalId(wavelet.getWaveletId())) {
            JsonArray docsJson = buildJsonDoc(wavelet);
            postUpdateToSolr(wavelet, docsJson);
        }
    }

    private void postUpdateToSolr(ReadableWaveletData wavelet, JsonArray docsJson) {
        PostMethod postMethod = new PostMethod(solrBaseUrl + "/update/json?commit=true");
        try {
            RequestEntity requestEntity = new StringRequestEntity(docsJson.toString(), "application/json", "UTF-8");
            postMethod.setRequestEntity(requestEntity);

            HttpClient httpClient = new HttpClient();
            int statusCode = httpClient.executeMethod(postMethod);
            if (statusCode != HttpStatus.SC_OK) {
                throw new IndexException(wavelet.getWaveId().serialise());
            }
        } catch (IOException e) {
            throw new IndexException(String.valueOf(wavelet.getWaveletId()), e);
        } finally {
            postMethod.releaseConnection();
        }
    }

    JsonArray buildJsonDoc(ReadableWaveletData wavelet) {
        JsonArray docsJson = new JsonArray();

        String waveletId = wavelet.getWaveletId().serialise();
        String modified = Long.toString(wavelet.getLastModifiedTime());
        String creator = wavelet.getCreator().getAddress();

        for (String docName : wavelet.getDocumentIds()) {
            ReadableBlipData document = wavelet.getDocument(docName);

            if (!IdUtil.isBlipId(docName)) {
                continue;
            }

            Iterable<DocInitialization> ops = Lists.newArrayList(document.getContent().asOperation());
            String text = Snippets.collateTextForOps(ops, new Function<StringBuilder, Void>() {

                @Override
                public Void apply(StringBuilder resultBuilder) {
                    resultBuilder.append("\n");
                    return null;
                }

            });

            JsonArray participantsJson = new JsonArray();
            for (ParticipantId participant : wavelet.getParticipants()) {
                String participantAddress = participant.toString();
                participantsJson.add(new JsonPrimitive(participantAddress));
            }

            String id = JavaWaverefEncoder
                    .encodeToUriPathSegment(WaveRef.of(wavelet.getWaveId(), wavelet.getWaveletId(), docName));

            JsonObject docJson = new JsonObject();
            docJson.addProperty(SolrSearchProviderImpl.ID, id);
            docJson.addProperty(SolrSearchProviderImpl.WAVE_ID, wavelet.getWaveId().serialise());
            docJson.addProperty(SolrSearchProviderImpl.WAVELET_ID, waveletId);
            docJson.addProperty(SolrSearchProviderImpl.DOC_NAME, docName);
            docJson.addProperty(SolrSearchProviderImpl.LMT, modified);
            docJson.add(SolrSearchProviderImpl.WITH, participantsJson);
            docJson.add(SolrSearchProviderImpl.WITH_FUZZY, participantsJson);
            docJson.addProperty(SolrSearchProviderImpl.CREATOR, creator);
            docJson.addProperty(SolrSearchProviderImpl.TEXT, text);
            docJson.addProperty(SolrSearchProviderImpl.IN, "inbox");

            docsJson.add(docJson);
        }
        return docsJson;
    }

    @Override
    public void waveletUpdate(final ReadableWaveletData wavelet, DeltaSequence deltas) {
        /*
         * Overridden out for optimization, see waveletCommitted(WaveletName,
         * HashedVersion)
         */
    }

    @Override
    public void waveletCommitted(final WaveletName waveletName, final HashedVersion version) {

        Preconditions.checkNotNull(waveletName);

        ListenableFutureTask<Void> task = ListenableFutureTask.create(new Callable<Void>() {

            @Override
            public Void call() throws Exception {
                ReadableWaveletData waveletData;
                try {
                    waveletData = waveletDataProvider.getReadableWaveletData(waveletName);
                    LOG.fine("commit " + version + " " + waveletData.getVersion());
                    if (waveletData.getVersion() == version.getVersion()) {
                        updateIndex(waveletData);
                    }
                } catch (WaveServerException e) {
                    LOG.log(Level.SEVERE, "Failed to update index for " + waveletName, e);
                    throw e;
                }
                return null;
            }
        });
        executor.execute(task);
    }

    @Override
    public synchronized void remakeIndex() throws WaveServerException {

        /*-
         * To fully rebuild the index, need to delete everything first
         * the <query> tag should contain the value of
         * org.waveprotocol.box.server.waveserver.SolrSearchProviderImpl.Q
         *
         * http://localhost:8983/solr/update?stream.body=<delete><query>waveId_s:[*%20TO%20*]%20AND%20waveletId_s:[*%20TO%20*]%20AND%20docName_s:[*%20TO%20*]%20AND%20lmt_l:[*%20TO%20*]%20AND%20with_ss:[*%20TO%20*]%20AND%20with_txt:[*%20TO%20*]%20AND%20creator_t:[*%20TO%20*]</query></delete>
         * http://localhost:8983/solr/update?stream.body=<commit/>
         *
         * see
         * http://wiki.apache.org/solr/FAQ#How_can_I_delete_all_documents_from_my_index.3F
         */

        sendRequestToDeleteSolrIndex();
        super.remakeIndex();
    }

    private void sendRequestToDeleteSolrIndex() {
        GetMethod getMethod = new GetMethod();
        try {
            getMethod.setURI(new URI(solrBaseUrl + "/update?wt=json" + "&stream.body=<delete><query>"
                    + SolrSearchProviderImpl.Q + "</query></delete>", false));

            HttpClient httpClient = new HttpClient();
            int statusCode = httpClient.executeMethod(getMethod);
            if (statusCode == HttpStatus.SC_OK) {
                getMethod.setURI(new URI(solrBaseUrl + "/update?wt=json" + "&stream.body=<commit/>", false));

                httpClient = new HttpClient();
                statusCode = httpClient.executeMethod(getMethod);
                if (statusCode != HttpStatus.SC_OK) {
                    LOG.warning("failed to clean solr index");
                }
            } else {
                LOG.warning("failed to clean solr index");
            }
        } catch (Exception e) {
            LOG.warning("failed to clean solr index", e);
        } finally {
            getMethod.releaseConnection();
        }
    }
}