/*
* Copyright (C) 2005-2012 BetaCONCEPT Limited
*
* This file is part of Astroboa.
*
* Astroboa is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Astroboa is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Astroboa. If not, see <http://www.gnu.org/licenses/>.
*/
package org.betaconceptframework.astroboa.console.commons;
import java.util.ArrayList;
import java.util.List;
import javax.faces.application.FacesMessage;
import org.betaconceptframework.astroboa.api.model.ContentObject;
import org.betaconceptframework.astroboa.api.model.exception.CmsException;
import org.betaconceptframework.astroboa.api.model.io.ResourceRepresentationType;
import org.betaconceptframework.astroboa.api.model.query.CmsOutcome;
import org.betaconceptframework.astroboa.api.model.query.criteria.ContentObjectCriteria;
import org.betaconceptframework.astroboa.api.service.ContentService;
import org.betaconceptframework.astroboa.console.jsf.PageController;
import org.betaconceptframework.astroboa.console.jsf.UIComponentBinding;
import org.betaconceptframework.astroboa.model.factory.CmsCriteriaFactory;
import org.betaconceptframework.ui.jsf.AbstractUIBean;
import org.betaconceptframework.ui.jsf.DataPage;
import org.betaconceptframework.ui.jsf.PagedListDataModel;
import org.betaconceptframework.ui.jsf.utility.JSFUtilities;
import org.jboss.seam.contexts.Contexts;
/**
* This class provides stateful as well as stateless content search facilities
* for front-end applications (i.e. those which present content to the user through a web interface.)
* It is a session spring bean and thus can be used in the context of a web application.
* It is merely a set of convenience methods to be used along the Astroboa ContentService API.
* The most useful ones are those with the suffix "WithPagedResults" which facilitate the retrieval of
* arbitrarily big number of results in pages which are lazily loaded and thus do not overload the system
* memory. Utilizing these methods when building UIs is very useful for the scalability of the solution.
* As a penalty for the lazy loading comes the statefulness of the class since the state of the criteria and the paged results should
* be remembered across sessions. In a later release we will turn this object into a conversation scoped Seam bean to allow applications with
* multiple windows to open search conversations in multiple windows (see remarks on localContentObjectCriteria for more details).
* Also there are some other stateful methods with the suffix "KeepResults" which do not produce paged results but keep the results in the bean
* to allow retrieval by presentation objects.
*
* @author Gregory Chomatas (gchomatas@betaconcept.com)
* @author Savvas Triantafyllou (striantafyllou@betaconcept.com)
*/
public class ContentObjectStatefulSearchService extends AbstractUIBean {
private static final long serialVersionUID = 1L;
// Injected Services
private ContentService contentService;
private ContentObjectUIWrapperFactory contentObjectUIWrapperFactory;
/*
* This object holds all the search criteria upon which searching/browsing
* for content objects in the repository is done This object is a pointer to
* the ContentObjectCriteria object that is passed to the
* searchForContentWithPagedResults method We need this copy in order to
* execute the query again when a new page of results is required IMPORTANT
* NOTICE: This object is a spring session bean holding the state of the user query
* and allowing to execute the same query along page requests to bring new
* pages of query results Therefore multiple simultaneous queries through
* different browser windows are not supported. We will always suppose that
* the user works in a single window and sequentially goes from a query to a
* new query. In the next release we will make it a Seam Conversation Bean to allow multiple
* queries in different windows.
* The localContentObjectCriteria is a copy of the initially provided criteria
* in order to prevent inconsistent queries if for some reason the initial object is accidentally altered
* while the user browses result pages.
*/
private ContentObjectCriteria localContentObjectCriteria;
// holds the total size of results which were returned by the query. It is
// required in order to built the result pages
private int searchResultSetSize;
// holds the paged search results
private PagedListDataModel<ContentObjectUIWrapper> returnedContentObjects;
// holds NON paged search results
private List<ContentObjectUIWrapper> nonPagedReturnedContentObjects;
//holds selected content objects
private ContentObjectSelectionBean contentObjectSelection = new ContentObjectSelectionBean();
/** We return the result to the calling object AND we also keep the results
* for further reference by presentation objects.
* The method is mainly indented for use by web UI code for retrieval of published objects.
*/
public List<ContentObjectUIWrapper> searchForContentAndKeepResults(
ContentObjectCriteria contentObjectCriteria,
boolean useDefaultRenderProperties, String locale) throws CmsException {
if (useDefaultRenderProperties)
setDefaultRenderPropertiesToContentObjectCriteria(contentObjectCriteria, locale);
CmsOutcome<ContentObject> cmsOutcome = contentService
.searchContentObjects(contentObjectCriteria, ResourceRepresentationType.CONTENT_OBJECT_LIST);
if (cmsOutcome.getCount() > 0) {
List<ContentObjectUIWrapper> wrappedContentObjects = new ArrayList<ContentObjectUIWrapper>();
List<ContentObject> cmsOutcomeRowList = cmsOutcome.getResults();
for (ContentObject contentObject : cmsOutcomeRowList) {
wrappedContentObjects.add(contentObjectUIWrapperFactory.getInstance(
contentObject));
}
setNonPagedReturnedContentObjects(wrappedContentObjects);
} else
setNonPagedReturnedContentObjects(null);
return getNonPagedReturnedContentObjects();
}
/**
* We just return the result to the calling object. We do not keep the
* results for further reference.
* @param contentObjectCriteria
* @return
* @throws CmsException
*/
public List<ContentObjectUIWrapper> searchForContent(
ContentObjectCriteria contentObjectCriteria,
boolean useDefaultRenderProperties, String locale) throws CmsException {
if (useDefaultRenderProperties)
setDefaultRenderPropertiesToContentObjectCriteria(contentObjectCriteria, locale);
CmsOutcome<ContentObject> cmsOutcome = contentService.searchContentObjects(contentObjectCriteria, ResourceRepresentationType.CONTENT_OBJECT_LIST);
if (cmsOutcome.getCount() > 0) {
List<ContentObjectUIWrapper> wrappedContentObjects = new ArrayList<ContentObjectUIWrapper>();
List<ContentObject> cmsOutcomeRowList = cmsOutcome.getResults();
for (ContentObject contentObject : cmsOutcomeRowList) {
wrappedContentObjects.add(contentObjectUIWrapperFactory.getInstance(contentObject));
}
return wrappedContentObjects;
} else
return null;
}
/*
public CmsOutcome<CmsSearchResult> searchByTopicIdWithUserFiltersUserOrderingAndPagedResults(
ContentObjectCriteria contentObjectCriteria) throws CmsException {
setLocalContentObjectCriteria(contentObjectCriteria);
// check if user filters are enabled
if (isFilteringDuringBrowsingEnabled())
// when browsing repository by Topic then all filters can be applied
setCriteriaFromUserSelection();
return searchForContentWithSecurityAndWithPagedResults(
getLocalContentObjectCriteria(), true, null);
}
public CmsOutcome<CmsSearchResult> searchByOwnerUUIDWithUserFiltersUserOrderingAndPagedResults(
ContentObjectCriteria contentObjectCriteria) throws CmsException {
setLocalContentObjectCriteria(contentObjectCriteria);
// check if user filters are enabled
if (isFilteringDuringBrowsingEnabled()) {
// when browsing repository by Owner then owner filter can not be
// applied
// we will apply only content type, text and date filters
setCriteriaFromContentObjectTypeSelection();
setCriteriaFromTextSelection();
setCriteriaFromDateSelection();
// Inform the user that some filters are not applicable for this
// type of search
JSFUtilities
.addMessage(
null,
"application.genericMessage",
new String[] { "Στην πλοήγηση ανά Ιδιοκτήτη το φίλτρο Ιδιοκτήτη Απενεργοποιείται " },
FacesMessage.SEVERITY_INFO);
}
return searchForContentWithSecurityAndWithPagedResults(
getLocalContentObjectCriteria(), true, null);
}
public CmsOutcome<CmsSearchResult> searchByContentObjectTypeWithUserFiltersUserOrderingAndPagedResults(
ContentObjectCriteria contentObjectCriteria) throws CmsException {
setLocalContentObjectCriteria(contentObjectCriteria);
// check if user filters are enabled
if (isFilteringDuringBrowsingEnabled()) {
// when browsing repository by content object type then content
// object type and date filter can not be applied
// we will apply only the owner and text filters
setCriteriaFromOwnerSelection();
setCriteriaFromTextSelection();
// Inform the user that some filters are not applicable for this
// type of search
JSFUtilities
.addMessage(
null,
"application.genericMessage",
new String[] { "Στην πλοήγηση ανά Τύπο Περιεχομένου τα Φίλτρα Τύπου Περιεχομένου και Ημερομηνίας Απενεργοποιειούντε " },
FacesMessage.SEVERITY_INFO);
}
return searchForContentWithSecurityAndWithPagedResults(
getLocalContentObjectCriteria(), true, null);
}
*/
/** Every search returns the total number of matched objects even if the objects we ask to render back in the result set are less than the total matched
* We need the total number of matched objects in order to build the PagedDataModel in the following lines.
* The PagedDataModel supports the lazy loading of results and their presentation in pages of a few ones each time so that very large results sets can be presented to the user
* without consuming all the system memory.
* The PagedDataModel will automatically set up the result set in each page fetch in order to lazy load pages of content objects as the user iterates
* through the pages of returned content objects
*/
public int searchForContentWithPagedResults(
ContentObjectCriteria contentObjectCriteria,
boolean useDefaultRenderProperties,
String locale, int pageSize) throws CmsException {
searchResultSetSize = 0;
localContentObjectCriteria = CmsCriteriaFactory.newContentObjectCriteria();
contentObjectCriteria.copyTo(localContentObjectCriteria);
if (useDefaultRenderProperties)
setDefaultRenderPropertiesToContentObjectCriteria(localContentObjectCriteria, locale);
CmsOutcome<ContentObject> cmsOutcome = contentService.searchContentObjects(localContentObjectCriteria, ResourceRepresentationType.CONTENT_OBJECT_LIST);
if (cmsOutcome.getCount() > 0) { // create the Lazy loading Data
// Model only if there are results
// we reset instead of nullify the results variable since nullification causes data scroller to not synchronize with the new data model
/*
if (returnedContentObjects != null){
returnedContentObjects.reset();
contentObjectSelection.clearAllSelectedContentObjects_UIAction();
}
else
returnedContentObjects = new ContentObjectDataModel(pageSize);
*/
returnedContentObjects = new ContentObjectDataModel(pageSize);
searchResultSetSize = (int) cmsOutcome.getCount();
List<ContentObject> cmsOutcomeRowList = cmsOutcome.getResults();
List<ContentObjectUIWrapper> wrappedContentObjects = new ArrayList<ContentObjectUIWrapper>();
for (ContentObject contentObject : cmsOutcomeRowList) {
wrappedContentObjects.add(contentObjectUIWrapperFactory.getInstance(contentObject));
}
DataPage<ContentObjectUIWrapper> dataPage = new DataPage<ContentObjectUIWrapper>(getSearchResultSetSize(), 0, wrappedContentObjects);
returnedContentObjects.setPage(dataPage);
}
else{
returnedContentObjects = null; // if no results are found we nullify the results variable to prevent the data scroller from trying to iterate through the data model
contentObjectSelection.clearAllSelectedContentObjects_UIAction();
}
return searchResultSetSize;
}
private void setDefaultRenderPropertiesToContentObjectCriteria(ContentObjectCriteria contentObjectCriteria, String locale) {
/*
* The default render properties when we retrieve content objects are:
* the localized labels are retrieved according to the provided locale
*/
contentObjectCriteria.getRenderProperties().resetRenderInstructions();
//contentObjectCriteria.getRenderProperties().renderValuesForLocale(locale);
}
public class ContentObjectDataModel extends PagedListDataModel<ContentObjectUIWrapper> {
public ContentObjectDataModel(int pageSize) {
super(pageSize);
}
public DataPage<ContentObjectUIWrapper> fetchPage(int startRow,
int pageSize) {
try {
// remove all selected objects
contentObjectSelection.clearAllSelectedContentObjects_UIAction();
List<ContentObjectUIWrapper> wrappedContentObjects;
//Limit now is always the same
//getLocalContentObjectCriteria().getResultRowRange().setRange(startRow, startRow + pageSize - 1);
//localContentObjectCriteria.getResultRowRange().setRange(startRow, pageSize);
localContentObjectCriteria.setOffsetAndLimit(startRow, pageSize);
long startTime = System.currentTimeMillis();
CmsOutcome<ContentObject> cmsOutcome = contentService
.searchContentObjects(localContentObjectCriteria, ResourceRepresentationType.CONTENT_OBJECT_LIST);
long endTime = System.currentTimeMillis();
getLogger().debug(
"Content Object Results Pager fetched the next " + pageSize + " objects in: "
+ (endTime - startTime) + "ms");
if (cmsOutcome.getCount() > 0) {
List<ContentObject> cmsOutcomeRowList = cmsOutcome
.getResults();
// List<ContentObject> contentObjects =
// getContentManager().getContentObjectByTopicUUID(selectedTopics,
// getResultsOrder(), new RowRange(startRow, startRow +
// pageSize -1), false, getOwnerUUIDsFilterList(),
// Condition.OR, getLocaleAsString()).getResults();
wrappedContentObjects = new ArrayList<ContentObjectUIWrapper>();
for (ContentObject contentObject : cmsOutcomeRowList) {
wrappedContentObjects.add(contentObjectUIWrapperFactory.getInstance(
contentObject));
}
DataPage<ContentObjectUIWrapper> dataPage = new DataPage<ContentObjectUIWrapper>(
getSearchResultSetSize(), startRow,
wrappedContentObjects);
return dataPage;
} else {
logger.error("The results pager retreived zero content objects for the requested result page.");
JSFUtilities
.addMessage(
null,
"application.unknown.error.message",
null,
FacesMessage.SEVERITY_WARN);
return null;
}
} catch (Exception e) {
JSFUtilities.addMessage(null,
"application.unknown.error.message",
null,
FacesMessage.SEVERITY_WARN);
logger.error("Error while loading content objects ", e);
return null;
}
}
}
public class ContentObjectDataModelForNonSecuredResults extends PagedListDataModel<ContentObjectUIWrapper> {
public ContentObjectDataModelForNonSecuredResults(int pageSize) {
super(pageSize);
}
public DataPage<ContentObjectUIWrapper> fetchPage(int startRow,
int pageSize) {
try {
List<ContentObjectUIWrapper> wrappedContentObjects;
//Limit now is always the same
//getLocalContentObjectCriteria().getResultRowRange().setRange(startRow, startRow + pageSize - 1);
//localContentObjectCriteria.getResultRowRange().setRange(startRow, pageSize);
localContentObjectCriteria.setOffsetAndLimit(startRow, pageSize);
long startTime = System.currentTimeMillis();
CmsOutcome<ContentObject> cmsOutcome = contentService
.searchContentObjects(localContentObjectCriteria, ResourceRepresentationType.CONTENT_OBJECT_LIST);
long endTime = System.currentTimeMillis();
getLogger().debug(
"Content Object Results Pager fetched the next " + pageSize + " objects in: "
+ (endTime - startTime) + "ms");
if (cmsOutcome.getCount() > 0) {
List<ContentObject> cmsOutcomeRowList = cmsOutcome
.getResults();
// List<ContentObject> contentObjects =
// getContentManager().getContentObjectByTopicUUID(selectedTopics,
// getResultsOrder(), new RowRange(startRow, startRow +
// pageSize -1), false, getOwnerUUIDsFilterList(),
// Condition.OR, getLocaleAsString()).getResults();
wrappedContentObjects = new ArrayList<ContentObjectUIWrapper>();
for (ContentObject contentObject : cmsOutcomeRowList) {
wrappedContentObjects.add(contentObjectUIWrapperFactory.getInstance(
contentObject));
}
DataPage<ContentObjectUIWrapper> dataPage = new DataPage<ContentObjectUIWrapper>(
getSearchResultSetSize(), startRow,
wrappedContentObjects);
return dataPage;
} else {
logger.error("The results pager retreived zero content objects for the requested result page");
JSFUtilities
.addMessage(
null,
"application.unknown.error.message",
null,
FacesMessage.SEVERITY_WARN);
return null;
}
} catch (Exception e) {
JSFUtilities.addMessage(null,
"application.unknown.error.message",
null,
FacesMessage.SEVERITY_WARN);
logger.error("Error while loading content objects ",e);
return null;
}
}
}
public void changeRowsPerDataTablePage_UIAction(int rowsPerPage){
PageController pageController = (PageController) JSFUtilities.getBeanFromSpringContext("pageController");
pageController.changeRowsPerDataTablePage(rowsPerPage);
// we need to run the query again with a new limit
localContentObjectCriteria.setOffsetAndLimit(0, rowsPerPage);
searchForContentWithPagedResults(localContentObjectCriteria, true, JSFUtilities.getLocaleAsString(), rowsPerPage);
UIComponentBinding uiComponentBinding = (UIComponentBinding) Contexts.getEventContext().get("uiComponentBinding");
if (uiComponentBinding != null){
uiComponentBinding.resetContentObjectTableScrollerComponent();
}
}
public void setLocalContentObjectCriteria(
ContentObjectCriteria localContentObjectCriteria) {
this.localContentObjectCriteria = localContentObjectCriteria;
}
public ContentObjectCriteria getLocalContentObjectCriteria() {
return localContentObjectCriteria;
}
public int getSearchResultSetSize() {
return searchResultSetSize;
}
public void setSearchResultSetSize(int searchResultSetSize) {
this.searchResultSetSize = searchResultSetSize;
}
public PagedListDataModel<ContentObjectUIWrapper> getReturnedContentObjects() {
return returnedContentObjects;
}
public void setReturnedContentObjects(
PagedListDataModel<ContentObjectUIWrapper> returnedContentObjects) {
this.returnedContentObjects = returnedContentObjects;
}
public void setContentService(ContentService contentService) {
this.contentService = contentService;
}
public List<ContentObjectUIWrapper> getNonPagedReturnedContentObjects() {
return nonPagedReturnedContentObjects;
}
public void setNonPagedReturnedContentObjects(
List<ContentObjectUIWrapper> nonPagedReturnedContentObjects) {
this.nonPagedReturnedContentObjects = nonPagedReturnedContentObjects;
}
public void setContentObjectUIWrapperFactory(
ContentObjectUIWrapperFactory contentObjectUIWrapperFactory) {
this.contentObjectUIWrapperFactory = contentObjectUIWrapperFactory;
}
/**
* @return the contentObjectSelection
*/
public ContentObjectSelectionBean getContentObjectSelection() {
return contentObjectSelection;
}
}