/* See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * Esri Inc. 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 com.esri.gpt.control.georss; import com.esri.gpt.catalog.search.ISearchFilter; import com.esri.gpt.catalog.search.SearchConfig; import com.esri.gpt.catalog.search.SearchCriteria; import com.esri.gpt.catalog.search.SearchException; import com.esri.gpt.catalog.search.SearchFilterHarvestSites; import com.esri.gpt.catalog.search.SearchFiltersList; import com.esri.gpt.catalog.search.SearchResult; import com.esri.gpt.catalog.search.SearchResultRecords; import com.esri.gpt.control.search.SearchController; import com.esri.gpt.framework.context.RequestContext; import com.esri.gpt.framework.jsf.FacesContextBroker; import com.esri.gpt.framework.util.Val; import java.util.ConcurrentModificationException; import java.util.logging.Level; import java.util.logging.Logger; import javax.faces.FactoryFinder; import javax.faces.application.Application; import javax.faces.application.NavigationHandler; import javax.faces.application.ViewHandler; import javax.faces.context.FacesContext; import javax.faces.lifecycle.Lifecycle; import javax.faces.lifecycle.LifecycleFactory; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * The Class HtmlAdvancedWriter. Generates the JSF page from rest. */ public class HtmlAdvancedWriter implements FeedWriter { // class variables ============================================================= /** The Constant JSFBEAN_SEARCH_CONTROLLER. */ private static final String JSFBEAN_SEARCH_CONTROLLER = "SearchController"; /** The Constant JSFBEAN_SEARCH_CONTROLLER. */ private static final String JSFBEAN_SEARCH_CRITERIA = "SearchCriteria"; /** The Search results page. */ private static final String SEARCH_RESULTS_PAGE = "/catalog/search/resultsBody.jsp"; /** The search page. */ private static final String SEARCH_PAGE = "/catalog/search/search.jsp"; /** Class logger *. */ private static final Logger LOG = Logger.getLogger(HtmlAdvancedWriter.class.getCanonicalName()); // instance variables ========================================================== /** The results only. */ private boolean resultsOnly; /** The criteria. */ private SearchCriteria criteria; private FacesContextBroker broker; private RequestContext requestContext; // properties ================================================================== /** * Checks if is results only. * * @return true, if is results only */ public boolean isResultsOnly() { return resultsOnly; } /** * Sets the results only. * * @param resultsOnly the new results only */ public void setResultsOnly(boolean resultsOnly) { this.resultsOnly = resultsOnly; } /** * Gets the criteria. * * @return the criteria (possibly null) */ public SearchCriteria getCriteria() { return criteria; } /** * Sets the criteria. * * @param criteria the new criteria */ public void setCriteria(SearchCriteria criteria) { this.criteria = criteria; } /** * Gets the request context. * * @return the request context */ public RequestContext getRequestContext() { return requestContext; } /** * Sets the request context. * * @param requestContext the new request context */ public void setRequestContext(RequestContext requestContext) { this.requestContext = requestContext; } // methods ===================================================================== /** * Write. * * @param records the records */ @Override public void write(IFeedRecords records) { SearchController controller = this.readController(); controller.getSearchResult().setRecords(new SearchResultRecords(records)); controller.setWasSearched(true); try { showResults(); } catch (Exception e) { LOG.log(Level.WARNING, "Error while showing results", e); } } /** * Write. * * @param result the result */ public void write(SearchResult result) { SearchController controller = this.readController(); controller.setSearchCriteria(this.getCriteria()); this.getCriteria().getSearchFilterPageCursor().setTotalRecordCount( result.getMaxQueryHits()); controller.setSearchResult(result); controller.setWasSearched(true); try { showResults(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * Read controller from the request. * * @return the search controller */ private SearchController readController() { FacesContextBroker broker = new FacesContextBroker(); @SuppressWarnings("unused") FacesContext fc = broker.getFacesContext(); // intentionally not used SearchController controller = (SearchController) broker.resolveManagedBean(JSFBEAN_SEARCH_CONTROLLER); controller.setSearchCriteria(this.getCriteria()); return controller; } /** * Read SearchCriteria from the session. * * @return the search controller */ private SearchCriteria readSessionCriteria() { FacesContextBroker broker = new FacesContextBroker(); @SuppressWarnings("unused") FacesContext fc = broker.getFacesContext(); fc.getApplication().getViewHandler().createView(fc, SEARCH_PAGE); // intentionally not used SearchCriteria criteria = (SearchCriteria) broker.resolveManagedBean(JSFBEAN_SEARCH_CRITERIA); return criteria; } /** * Show results. * * @throws Exception * the exception */ protected void showResults() throws Exception { FacesContextBroker broker = new FacesContextBroker(); String dispatchTo = ""; String tagName = "com.esri.gpt.control.filter.EncodingFilterTag"; // will prevent going to front page this.getRequestContext().addToSession(tagName, "tag"); HttpServletRequest httpReq = broker.extractHttpServletRequest(); HttpServletResponse httpResp = broker.extractHttpServletResponse(); FacesContext fctx = broker.getFacesContext(); Application application = fctx.getApplication(); if (this.isResultsOnly()) { dispatchTo = SEARCH_RESULTS_PAGE; } else { // TODO: Glassfish does not support this section (f=searchpage) NavigationHandler navHandler = application.getNavigationHandler(); navHandler.handleNavigation(fctx, null, "catalog.search.results"); dispatchTo = fctx.getViewRoot().getViewId(); String jsfSuffix = SearchConfig.getConfiguredInstance().getJsfSuffix(); if ("".equals(jsfSuffix)) { jsfSuffix = ".page"; } if (jsfSuffix.indexOf('.') < 0) { jsfSuffix = "." + jsfSuffix; } //javax.faces.DEFAULT_SUFFIX dispatchTo = dispatchTo.replaceAll(".jsp", jsfSuffix); //ViewHandler.DEFAULT_SUFFIX_PARAM_NAME = jsfSuffix; //fctx.getExternalContext().dispatch(dispatchTo); //httpReq.getRequestDispatcher( dispatchTo).forward(httpReq, httpResp); } // Synching criteria with session criteria in thread so that search result // does // not have to wait for synch to complete. setExtraCriteriaProperties(httpReq); SynchSessionCriteria synchSc = new SynchSessionCriteria(criteria, this .readSessionCriteria()); synchSc.synch(); //Thread thread = new Thread(synchSc); //thread.start(); jsfDispatchPage(fctx, dispatchTo); } /** * Jsf dispatch page. * * @param fctx the faces context * @param page the pge */ private void jsfDispatchPage(FacesContext fctx, String page) { LifecycleFactory lf = (LifecycleFactory) FactoryFinder .getFactory(FactoryFinder.LIFECYCLE_FACTORY); Lifecycle lifecycle = lf.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE); ViewHandler vh = fctx.getApplication().getViewHandler(); fctx.getViewRoot().setRenderKitId(vh.calculateRenderKitId(fctx)); fctx.setViewRoot(vh.createView(fctx, page)); // view rendering try { lifecycle.render(fctx); } catch (Exception e) { LOG.log(Level.INFO, "Error while rendering page. Attempting again" + page, e); lifecycle.render(fctx); } finally { fctx.release(); } } /** * Sets the extra criteria properties. Properties unique to using * this writer. Currently gets the rids and the ridName attribute. * * @pa ram httpReq the new extra criteria properties */ private void setExtraCriteriaProperties(HttpServletRequest httpReq) { String rids = httpReq.getParameter("rids"); String ridName = httpReq.getParameter("ridName"); SearchCriteria criteria = this.getCriteria(); SearchFiltersList filters = criteria.getMiscelleniousFilters(); for(ISearchFilter filter:filters) { if(filter instanceof SearchFilterHarvestSites) { SearchFilterHarvestSites sfHs = (SearchFilterHarvestSites) filter; sfHs.setSelectedDistributedIds(rids); sfHs.setSelectedHarvestSiteName(ridName); } } boolean expandResults = Val.chkBool(httpReq.getParameter("expandResults"), false); criteria.setExpandResultContent(expandResults); } /** * Class to update the session criteria * * @author TM * */ private class SynchSessionCriteria implements Runnable { private SearchCriteria __suppliedCriteria; private SearchCriteria __sessionCriteria; /** * Instantiates a new synch session criteria. * * @param suppliedCriteria the supplied criteria * @param sessionCriteria the session criteria */ private SynchSessionCriteria(SearchCriteria suppliedCriteria, SearchCriteria sessionCriteria){ __suppliedCriteria = criteria; __sessionCriteria = sessionCriteria; } /** * Thread runner. Calls the synch method. */ public void run() { try { synch(); } catch(Throwable e) { LOG.log(Level.WARNING, "Error while synchronizing rest criteria with " + "session criteria", e ); } } /** * Synch. * * @throws SearchException the search exception */ public void synch() throws SearchException { if(__suppliedCriteria == __sessionCriteria) { return; } try { __sessionCriteria.loadSearchCriteria(__suppliedCriteria.toDom()); } catch(ConcurrentModificationException e) { // This sometimes occurs especially when the user presesses the search // button multiple times at a very fast pace LOG.fine("Saving session failed" + e.getMessage()); } } } }