Java tutorial
/* * 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() { } } }