annis.gui.QueryController.java Source code

Java tutorial

Introduction

Here is the source code for annis.gui.QueryController.java

Source

/*
 * Copyright 2013 Corpuslinguistic working group Humboldt University Berlin.
 *
 * 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.
 */
package annis.gui;

import annis.libgui.Helper;
import annis.gui.beans.HistoryEntry;
import annis.libgui.media.MediaController;
import annis.gui.model.PagedResultQuery;
import annis.gui.model.Query;
import annis.gui.paging.PagingCallback;
import annis.gui.paging.PagingComponent;
import annis.gui.resultview.ResultViewPanel;
import annis.libgui.visualizers.IFrameResourceMap;
import annis.service.objects.Match;
import annis.service.objects.MatchAndDocumentCount;
import com.github.wolfie.refresher.Refresher;
import com.sun.jersey.api.client.AsyncWebResource;
import com.sun.jersey.api.client.GenericType;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.vaadin.server.VaadinSession;
import com.vaadin.ui.Notification;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.commons.collections15.set.ListOrderedSet;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Manages all the query related actions.
 *
 * <strong>This class is not reentrant.</strong>
 * It is expected that you call the functions from the Vaadin session lock context,
 * either implicitly (e.g. from a component constructor or a handler callback)
 * or explicitly with {@link VaadinSession#lock() }.
 *
 * @author Thomas Krause <thomas.krause@alumni.hu-berlin.de>
 */
public class QueryController implements PagingCallback, Refresher.RefreshListener {
    private static final Logger log = LoggerFactory.getLogger(ResultViewPanel.class);

    private SearchUI ui;

    private PagedResultQuery lastQuery;
    private ListOrderedSet<HistoryEntry> history;
    private ResultViewPanel lastResultView;
    private MatchAndDocumentCount lastCount;

    private Future<MatchAndDocumentCount> futureCount;
    private Future<List<Match>> futureMatches;

    public QueryController(SearchUI ui) {
        this.ui = ui;
        this.history = new ListOrderedSet<HistoryEntry>();
    }

    public void updateCorpusSetList() {
        ui.getControlPanel().getCorpusList().updateCorpusSetList();
    }

    public void setQuery(String query) {
        setQuery(new Query(query, ui.getControlPanel().getCorpusList().getSelectedCorpora()));
    }

    public void setQuery(Query query) {
        PagedResultQuery paged = new PagedResultQuery(ui.getControlPanel().getSearchOptions().getLeftContext(),
                ui.getControlPanel().getSearchOptions().getRightContext(), 0,
                ui.getControlPanel().getSearchOptions().getResultsPerPage(),
                ui.getControlPanel().getSearchOptions().getSegmentationLayer(), query.getQuery(),
                query.getCorpora());
        setQuery(paged);
    }

    public void setQuery(PagedResultQuery query) {
        // only allow offset at multiples of the limit size
        query.setOffset(query.getOffset() - (query.getOffset() % query.getLimit()));

        lastQuery = query;

        ui.getControlPanel().getQueryPanel().setQuery(query.getQuery());
        ui.getControlPanel().getSearchOptions().setLeftContext(query.getContextLeft());
        ui.getControlPanel().getSearchOptions().setRightContext(query.getContextRight());
        ui.getControlPanel().getSearchOptions().setSegmentationLayer(query.getSegmentation());

        if (query.getCorpora() != null) {
            ui.getControlPanel().getCorpusList().selectCorpora(query.getCorpora());
        }
    }

    public void executeQuery() {
        executeQuery(true, true);
    }

    /**
     * Cancel queries from the client side.
     *
     * Important: This does not magically cancel the query on the server side,
     * so don't use this to implement a "real" query cancelation.
     */
    private void cancelQueries() {
        // don't spin forever when canceled
        ui.getControlPanel().getQueryPanel().setCountIndicatorEnabled(false);

        if (lastResultView != null && lastQuery != null) {
            // explicitly show empty result
            lastResultView.setResult(null, lastQuery.getContextLeft(), lastQuery.getContextRight(),
                    lastQuery.getSegmentation(), lastQuery.getOffset());
        }

        // disable the refresher
        ui.setRefresherEnabled(false);

        // abort last tasks if running
        if (futureCount != null && !futureCount.isDone()) {
            futureCount.cancel(true);
        }
        if (futureMatches != null && !futureMatches.isDone()) {
            futureMatches.cancel(true);

        }

        futureCount = null;
        futureMatches = null;

    }

    public void executeQuery(boolean executeCount, boolean executeResult) {

        Validate.notNull(lastQuery, "You have to set a query before you can execute it.");

        cancelQueries();

        // cleanup resources
        VaadinSession session = VaadinSession.getCurrent();
        session.setAttribute(IFrameResourceMap.class, new IFrameResourceMap());
        if (session.getAttribute(MediaController.class) != null) {
            session.getAttribute(MediaController.class).clearMediaPlayers();
        }

        ui.updateFragment(lastQuery);

        HistoryEntry e = new HistoryEntry();
        e.setCorpora(lastQuery.getCorpora());
        e.setQuery(lastQuery.getQuery());
        // remove it first in order to let it appear on the beginning of the list
        history.remove(e);
        history.add(0, e);
        ui.getControlPanel().getQueryPanel().updateShortHistory(history.asList());

        if (lastQuery.getCorpora() == null || lastQuery.getCorpora().isEmpty()) {
            Notification.show("Please select a corpus", Notification.Type.WARNING_MESSAGE);
            return;
        }
        if ("".equals(lastQuery.getQuery())) {
            Notification.show("Empty query", Notification.Type.WARNING_MESSAGE);
            return;
        }

        AsyncWebResource res = Helper.getAnnisAsyncWebResource();

        if (executeResult) {
            // remove old result from view
            if (lastResultView != null) {
                ui.getMainTab().removeComponent(lastResultView);
            }
            lastResultView = new ResultViewPanel(this, ui, ui.getInstanceConfig());
            ui.getMainTab().addTab(lastResultView, "Query Result");
            ui.getMainTab().setSelectedTab(lastResultView);

            futureMatches = res.path("query").path("search").path("find").queryParam("q", lastQuery.getQuery())
                    .queryParam("offset", "" + lastQuery.getOffset()).queryParam("limit", "" + lastQuery.getLimit())
                    .queryParam("corpora", StringUtils.join(lastQuery.getCorpora(), ",")).get(new MatchListType());

            ui.setRefresherEnabled(true);

        }

        if (executeCount) {
            // start count query
            ui.getControlPanel().getQueryPanel().setCountIndicatorEnabled(true);

            futureCount = res.path("query").path("search").path("count").queryParam("q", lastQuery.getQuery())
                    .queryParam("corpora", StringUtils.join(lastQuery.getCorpora(), ","))
                    .get(MatchAndDocumentCount.class);

            ui.setRefresherEnabled(true);
        }
    }

    public void corpusSelectionChanged() {
        ui.getControlPanel().getSearchOptions()
                .updateSegmentationList(ui.getControlPanel().getCorpusList().getSelectedCorpora());

    }

    public Set<String> getSelectedCorpora() {
        return ui.getControlPanel().getCorpusList().getSelectedCorpora();
    }

    public PagedResultQuery getQuery() {
        return lastQuery;
    }

    @Override
    public void switchPage(int offset, int limit) {
        if (lastQuery != null) {
            lastQuery.setOffset(offset);
            lastQuery.setLimit(limit);

            // execute the result query again
            executeQuery(false, true);
            if (lastResultView != null && lastCount != null) {
                lastResultView.setCount(lastCount.getMatchCount());
            }
        }
    }

    /**
     * Returns true if any query (count or find) is running.
     */
    public boolean isQueryRunning() {
        return futureCount != null || futureMatches != null;
    }

    @Override
    public void refresh(Refresher source) {
        if (source.getRefreshInterval() <= 0) {
            // do nothing if refresher is not longer activated
            return;
        }
        boolean matchFinished = true;
        if (futureMatches != null) {
            matchFinished = checkFutureMatchesFinished();
        }

        // initialize to true value in case futureCount is null
        boolean countFinished = true;
        if (futureCount != null) {
            countFinished = checkFutureCount();
        }

        if (matchFinished && countFinished) {
            // we are finished loading the results, do not refresh any longer
            ui.setRefresherEnabled(false);
        }
    }

    private boolean checkFutureMatchesFinished() {
        List<Match> result = null;
        try {
            result = futureMatches.get(100, TimeUnit.MILLISECONDS);
        } catch (InterruptedException ex) {
            log.warn(null, ex);
        } catch (ExecutionException root) {
            if (lastResultView != null && lastResultView.getPaging() != null) {
                PagingComponent paging = lastResultView.getPaging();

                Throwable cause = root.getCause();

                if (cause instanceof UniformInterfaceException) {
                    UniformInterfaceException ex = (UniformInterfaceException) cause;

                    if (ex.getResponse().getStatus() == 400) {
                        paging.setInfo("parsing error: " + ex.getResponse().getEntity(String.class));
                    } else if (ex.getResponse().getStatus() == 504) // gateway timeout
                    {
                        paging.setInfo("Timeout: query exeuction took too long");
                    } else {
                        paging.setInfo("unknown error: " + ex);
                    }
                } else {
                    log.error("Unexcepted ExecutionException cause", root);
                }

            }
        } catch (TimeoutException ex) {
            // we ignore this
            return false;
        }

        lastResultView.setResult(result, lastQuery.getContextLeft(), lastQuery.getContextRight(),
                lastQuery.getSegmentation(), lastQuery.getOffset());
        futureMatches = null;

        return true;
    }

    private boolean checkFutureCount() {
        try {
            lastCount = futureCount.get(100, TimeUnit.MILLISECONDS);
            String documentString = lastCount.getDocumentCount() > 1 ? "documents" : "document";
            String matchesString = lastCount.getMatchCount() > 1 ? "matches" : "match";

            ui.getControlPanel().getQueryPanel().setStatus("" + lastCount.getMatchCount() + " " + matchesString
                    + " <br/>in " + lastCount.getDocumentCount() + " " + documentString);
            if (lastResultView != null && lastCount.getMatchCount() > 0) {
                lastResultView.setCount(lastCount.getMatchCount());
            }

        } catch (InterruptedException ex) {
            log.warn(null, ex);
        } catch (ExecutionException root) {
            Throwable cause = root.getCause();

            if (cause instanceof UniformInterfaceException) {
                UniformInterfaceException ex = (UniformInterfaceException) cause;
                if (ex.getResponse().getStatus() == 400) {
                    Notification.show("parsing error", ex.getResponse().getEntity(String.class),
                            Notification.Type.WARNING_MESSAGE);
                } else if (ex.getResponse().getStatus() == 504) // gateway timeout
                {
                    Notification.show("Timeout: query execution took too long.",
                            "Try to simplyfiy your query e.g. by replacing \"node\" with an annotation name or adding more constraints between the nodes.",
                            Notification.Type.WARNING_MESSAGE);
                } else {
                    Notification.show("unknown error " + ex.getResponse().getStatus(),
                            ex.getResponse().getEntity(String.class), Notification.Type.WARNING_MESSAGE);
                }
            } else {
                log.error("Unexcepted ExecutionException cause", root);
            }
        } catch (TimeoutException ex) {
            // we ignore this
            return false;
        }

        futureCount = null;
        ui.getControlPanel().getQueryPanel().setCountIndicatorEnabled(false);
        return true;
    }

    private static class MatchListType extends GenericType<List<Match>> {

        public MatchListType() {
        }
    }
}