BaseSearchServiceConnector.java
/*
* Copyright (C) 2003-2013 eXo Platform SAS.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.exoplatform.services.wcm.search.connector;
import java.text.DateFormat;
import java.text.Normalizer;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import org.exoplatform.commons.api.search.SearchServiceConnector;
import org.exoplatform.commons.api.search.data.SearchContext;
import org.exoplatform.commons.api.search.data.SearchResult;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.container.xml.InitParams;
import org.exoplatform.portal.config.UserPortalConfig;
import org.exoplatform.portal.config.UserPortalConfigService;
import org.exoplatform.portal.mop.SiteKey;
import org.exoplatform.portal.mop.user.UserNavigation;
import org.exoplatform.portal.mop.user.UserPortalContext;
import org.exoplatform.services.cms.documents.DocumentService;
import org.exoplatform.services.cms.drives.DriveData;
import org.exoplatform.services.cms.drives.ManageDriveService;
import org.exoplatform.services.cms.impl.Utils;
import org.exoplatform.services.jcr.RepositoryService;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.security.ConversationState;
import org.exoplatform.services.security.IdentityConstants;
import org.exoplatform.services.wcm.core.NodetypeConstant;
import org.exoplatform.services.wcm.search.QueryCriteria;
import org.exoplatform.services.wcm.search.ResultNode;
import org.exoplatform.services.wcm.search.SiteSearchService;
import org.exoplatform.services.wcm.search.base.AbstractPageList;
import org.exoplatform.services.wcm.search.base.EcmsSearchResult;
import org.exoplatform.services.wcm.utils.WCMCoreUtils;
/**
* This abstract class is extended by the SearchService connectors which provide search result for a specific content type
*/
public abstract class BaseSearchServiceConnector extends SearchServiceConnector {
public static final String sortByDate = "date";
public static final String sortByRelevancy = "relevancy";
public static final String sortByTitle = "title";
protected SiteSearchService siteSearch_;
protected DocumentService documentService;
protected ManageDriveService driveService_;
private static final Log LOG = ExoLogger.getLogger(BaseSearchServiceConnector.class.getName());
public static final String DEFAULT_SITENAME = "intranet";
public static final String PAGE_NAGVIGATION = "documents";
public static final String NONE_NAGVIGATION = "#";
public static final String PORTLET_NAME = "FileExplorerPortlet";
public BaseSearchServiceConnector(InitParams initParams) throws Exception {
super(initParams);
siteSearch_ = WCMCoreUtils.getService(SiteSearchService.class);
documentService = WCMCoreUtils.getService(DocumentService.class);
driveService_ = WCMCoreUtils.getService(ManageDriveService.class);
}
/**
* The connectors must implement this search method, with the following parameters and return a collection of SearchResult
*
* @param context Search context
* @param query The user-input query to search for
* @param sites Search on these specified sites only (e.g acme, intranet...)
* @param offset Start offset of the result set
* @param limit Maximum size of the result set
* @param sort The field to sort the result set
* @param order Sort order (ASC, DESC)
* @return A collection of SearchResult
*/
@Override
public Collection<SearchResult> search(SearchContext context,
String query,
Collection<String> sites,
int offset,
int limit,
String sort,
String order) {
Collection<SearchResult> ret = new ArrayList<SearchResult>();
//prepare input parameters for search
if (query != null) {
query = query.trim();
}
QueryCriteria criteria = createQueryCriteria(query, offset, limit, sort, order);
//query search result
try {
criteria.setSiteName(getSitesStr(sites));
ret = convertResult(searchNodes(criteria, context), limit, offset, context);
} catch (Exception e) {
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessage(), e);
}
}
return ret;
}
/**
* convert Collections<String> to string, elements are seperated by commas
* @param sites the collection
* @return the string
*/
private String getSitesStr(Collection<String> sites) {
if (sites == null || sites.size() == 0) return null;
StringBuffer s = new StringBuffer();
for (String site : sites) {
s.append(site).append(',');
}
return s.substring(0, s.length() - 1);
}
/**
* creates the QueryCriteria object based on the search service
* @param query the query string
* @param offset the offset
* @param limit the limit
* @param sort sort field
* @param order order by
* @return the QueryCriteria
*/
protected abstract QueryCriteria createQueryCriteria(String query, long offset, long limit, String sort, String order);
/**
* searches base on the search service type
* @param criteria the query criteria
* @return page list containing the result
*/
protected abstract AbstractPageList<ResultNode> searchNodes(QueryCriteria criteria, SearchContext context) throws Exception;
/**
* filters the node base on search type: document or file
* @return the node object
*/
protected abstract ResultNode filterNode(ResultNode node) throws RepositoryException;
/**
* converts data: from {@code PageList<ResultNode> to List<SearchResult>}
* @param pageList
* @return
*/
protected List<SearchResult> convertResult(AbstractPageList<ResultNode> pageList, int limit, int offset, SearchContext context) {
List<SearchResult> ret = new ArrayList<SearchResult>();
try {
if (pageList != null) {
for (int i = 1; i <= pageList.getAvailablePage(); i++) {
List<ResultNode> list = pageList.getPageWithOffsetCare(i);
if (list == null || list.size() == 0) return ret;
for (Object obj : list) {
try {
if (obj instanceof ResultNode) {
ResultNode retNode = filterNode((ResultNode)obj);
if (retNode == null) {
continue;
}
//generate SearchResult object
Calendar date = getDate(retNode);
String url = getPath(retNode, context);
if (url == null) continue;
EcmsSearchResult result =
// new SearchResult(url, title, excerpt, detail, imageUrl, date, relevancy);
new EcmsSearchResult(url,
getPreviewUrl(retNode, context),
getTitleResult(retNode),
retNode.getExcerpt(),
getDetails(retNode, context),
getImageUrl(retNode),
date.getTimeInMillis(),
(long)retNode.getScore(),
getFileType(retNode),
retNode.getPath());
if (result != null) {
ret.add(result);
}
if (ret.size() >= limit) {
return ret;
}
}//if
} catch (Exception e) {
if (LOG.isErrorEnabled()) {
LOG.error("Failed to get result information.", e);
}
}
}//for inner
} //for outer
}//if
} catch (Exception e) {
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessage(), e);
}
}
return ret;
}
/**---------------------------HELPER METHODS----------------------------------*/
/**
* gets the file size
* @param node The node
* @return the file size
* @throws Exception
*/
protected String fileSize(Node node) throws Exception {
return Utils.fileSize(node);
}
/**
* returns the date information of node
* @param node the node
* @return the date
* @throws Exception
*/
protected Calendar getDate(Node node) throws Exception {
return node.hasProperty(NodetypeConstant.EXO_LAST_MODIFIED_DATE) ?
node.getProperty(NodetypeConstant.EXO_LAST_MODIFIED_DATE).getDate() :
node.hasProperty(NodetypeConstant.EXO_DATE_CREATED) ?
node.getProperty(NodetypeConstant.EXO_DATE_CREATED).getDate() :
Calendar.getInstance();
}
/**
* formats the date object in simple date format
* @param date the Date object
* @return the String representation
*/
protected String formatDate(Calendar date) {
DateFormat format = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.FULL, SimpleDateFormat.SHORT);
return " - " + format.format(date.getTime());
}
protected String getDriveTitle(DriveData driveData) {
if (driveData == null) {
return "";
}
String id = driveData.getName();
String path = driveData.getResolvedHomePath();
//get space name (in case drive is space drive)
try {
Class spaceServiceClass = Class.forName("org.exoplatform.social.core.space.spi.SpaceService");
Object spaceService = ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(spaceServiceClass);
Class spaceClass = Class.forName("org.exoplatform.social.core.space.model.Space");
Object space = spaceServiceClass.getDeclaredMethod("getSpaceByGroupId", String.class)
.invoke(spaceService, id.replace(".", "/"));
if (space != null) {
return String.valueOf(spaceClass.getDeclaredMethod("getDisplayName").invoke(space));
}
} catch (Exception e) {
//can not get space, do nothing, will return the drive label
if (LOG.isInfoEnabled()) {
LOG.info("Can not find the space corresponding to drive " + id);
}
}
//get drive label
try {
RepositoryService repoService = WCMCoreUtils.getService(RepositoryService.class);
Node groupNode = (Node)WCMCoreUtils.getSystemSessionProvider().getSession(
repoService.getCurrentRepository().getConfiguration().getDefaultWorkspaceName(),
repoService.getCurrentRepository()).getItem(path);
while (groupNode.getParent() != null) {
if (groupNode.hasProperty(NodetypeConstant.EXO_LABEL)) {
return groupNode.getProperty(NodetypeConstant.EXO_LABEL).getString();
} else groupNode = groupNode.getParent();
}
return id.replace(".", " / ");
} catch(Exception e) {
return id.replace(".", " / ");
}
}
/**
* Remove accents from query
*
* @param query
* @return
*/
protected static String removeAccents(String query) {
query = Normalizer.normalize(query, Normalizer.Form.NFD);
query = query.replaceAll("[\\p{InCombiningDiacriticalMarks}]", "");
return query;
}
/**
* returns path of node in format: "{drivename}/{relative path from drive root node}
* @param node the node
* @return the expected path
* @throws Exception
*/
protected abstract String getPath(ResultNode node, SearchContext context) throws Exception;
/**
* returns the preview url
* @param node the node
* @return the expected path
* @throws Exception
*/
protected String getPreviewUrl(ResultNode node, SearchContext context) throws Exception {
// defaults to the same url returned by getPath
return getPath(node, context);
}
/**
* gets the file type
* @return
* @throws Exception
*/
protected abstract String getFileType(ResultNode node) throws Exception;
/**
* gets the title of result, based on the result type
* @param node
* @return
* @throws Exception
*/
protected abstract String getTitleResult(ResultNode node) throws Exception;
/**
* gets the image url
* @return
*/
protected abstract String getImageUrl(Node node);
/**
* gets the detail about result node
* @param node the node
* @throws Exception
*/
protected abstract String getDetails(ResultNode node, SearchContext context) throws Exception;
}