QueryServiceImpl.java
/*
* Copyright (C) 2003-2007 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see<http://www.gnu.org/licenses/>.
*/
package org.exoplatform.services.cms.queries.impl;
import org.exoplatform.commons.utils.ISO8601;
import org.exoplatform.container.xml.InitParams;
import org.exoplatform.container.xml.PortalContainerInfo;
import org.exoplatform.services.cache.CacheService;
import org.exoplatform.services.cache.ExoCache;
import org.exoplatform.services.cms.BasePath;
import org.exoplatform.services.cms.impl.DMSConfiguration;
import org.exoplatform.services.cms.impl.DMSRepositoryConfiguration;
import org.exoplatform.services.cms.queries.QueryService;
import org.exoplatform.services.jcr.RepositoryService;
import org.exoplatform.services.jcr.access.PermissionType;
import org.exoplatform.services.jcr.core.ExtendedNode;
import org.exoplatform.services.jcr.core.ManageableRepository;
import org.exoplatform.services.jcr.ext.common.SessionProvider;
import org.exoplatform.services.jcr.ext.hierarchy.NodeHierarchyCreator;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.organization.MembershipHandler;
import org.exoplatform.services.organization.OrganizationService;
import org.exoplatform.services.security.ConversationState;
import org.exoplatform.services.security.IdentityConstants;
import org.exoplatform.services.security.Identity;
import org.exoplatform.services.wcm.utils.WCMCoreUtils;
import org.exoplatform.web.application.RequestContext;
import org.picocontainer.Startable;
import javax.jcr.*;
import javax.jcr.query.Query;
import javax.jcr.query.QueryManager;
import javax.jcr.query.QueryResult;
import java.util.*;
public class QueryServiceImpl implements QueryService, Startable{
private static final String[] perms = {PermissionType.READ, PermissionType.ADD_NODE,
PermissionType.SET_PROPERTY, PermissionType.REMOVE };
private String relativePath_;
private List<QueryPlugin> queryPlugins_ = new ArrayList<QueryPlugin> ();
private RepositoryService repositoryService_;
private ExoCache<String, QueryResult> cache_;
private PortalContainerInfo containerInfo_;
private OrganizationService organizationService_;
private String baseUserPath_;
private String baseQueriesPath_;
private String group_;
private DMSConfiguration dmsConfiguration_;
private Set<String> configuredQueries_;
private NodeHierarchyCreator nodeHierarchyCreator_;
private static final Log LOG = ExoLogger.getLogger(QueryServiceImpl.class.getName());
/**
* Constructor method
* @param repositoryService
* @param nodeHierarchyCreator
* @param params
* @param containerInfo
* @param cacheService
* @param organizationService
* @param dmsConfiguration
* @throws Exception
*/
public QueryServiceImpl(RepositoryService repositoryService, NodeHierarchyCreator nodeHierarchyCreator,
InitParams params, PortalContainerInfo containerInfo, CacheService cacheService,
OrganizationService organizationService, DMSConfiguration dmsConfiguration) throws Exception {
relativePath_ = params.getValueParam("relativePath").getValue();
group_ = params.getValueParam("group").getValue();
repositoryService_ = repositoryService;
containerInfo_ = containerInfo;
cache_ = cacheService.getCacheInstance(CACHE_NAME);
organizationService_ = organizationService;
nodeHierarchyCreator_ = nodeHierarchyCreator;
baseUserPath_ = nodeHierarchyCreator.getJcrPath(BasePath.CMS_USERS_PATH);
baseQueriesPath_ = nodeHierarchyCreator.getJcrPath(BasePath.QUERIES_PATH);
dmsConfiguration_ = dmsConfiguration;
}
/**
* Implemented method from Startable class
* init all ManageDrivePlugin
* @see QueryPlugin
*/
public void start() {
configuredQueries_ = new HashSet<String>();
for(QueryPlugin queryPlugin : queryPlugins_){
try{
queryPlugin.init(baseQueriesPath_);
configuredQueries_.addAll(queryPlugin.getAllConfiguredQueries());
}catch (Exception e) {
if (LOG.isErrorEnabled()) {
LOG.error("Can not start query plugin '" + queryPlugin.getName() + "'", e);
}
}
}
}
/**
* Implemented method from Startable class
*/
public void stop() {
}
/**
* Init query node with current repository
*/
public void init() throws Exception {
configuredQueries_ = new HashSet<String>();
for(QueryPlugin queryPlugin : queryPlugins_){
try{
queryPlugin.init(baseQueriesPath_);
configuredQueries_.addAll(queryPlugin.getAllConfiguredQueries());
}catch (Exception e) {
if (LOG.isErrorEnabled()) {
LOG.error("Can not init query plugin '" + queryPlugin.getName() + "'", e);
}
}
}
}
/**
* Add new QueryPlugin to queryPlugins_
* @see QueryPlugin
* @param queryPlugin QueryPlugin
*/
public void setQueryPlugin(QueryPlugin queryPlugin) {
queryPlugins_.add(queryPlugin);
}
/**
* {@inheritDoc}
*/
public String getRelativePath() { return relativePath_; }
/**
* {@inheritDoc}
*/
public List<Query> getQueries(String userName, SessionProvider provider) throws Exception {
List<Query> queries = new ArrayList<Query>();
if (userName == null) return queries;
Session session = getSession(provider, true);
QueryManager manager = session.getWorkspace().getQueryManager();
Node usersHome;
try {
usersHome = (Node)session.getItem(baseUserPath_);
} catch (PathNotFoundException e) {
usersHome = (Node)getSession(provider, false).getItem(baseUserPath_);
}
Node userHome = null;
try {
userHome = nodeHierarchyCreator_.getUserNode(provider, userName);
} catch(Exception e) {
if (LOG.isWarnEnabled()) {
LOG.warn(e.getMessage());
}
}
if (userHome == null) {
if(usersHome.hasNode(userName)) {
userHome = usersHome.getNode(userName);
} else{
userHome = usersHome.addNode(userName);
if(userHome.canAddMixin("exo:privilegeable")){
userHome.addMixin("exo:privilegeable");
}
((ExtendedNode)userHome).setPermissions(getPermissions(userName));
Node query = null;
if(userHome.hasNode(relativePath_)) {
query = userHome.getNode(relativePath_);
} else {
query = getNodeByRelativePath(userHome, relativePath_);
}
if (query.canAddMixin("exo:privilegeable")){
query.addMixin("exo:privilegeable");
}
((ExtendedNode)query).setPermissions(getPermissions(userName));
usersHome.save();
}
}
Node queriesHome = null;
if(userHome.hasNode(relativePath_)) {
queriesHome = userHome.getNode(relativePath_);
} else {
queriesHome = getNodeByRelativePath(userHome, relativePath_);
}
NodeIterator iter = queriesHome.getNodes();
while (iter.hasNext()) {
Node node = iter.nextNode();
if(node.isNodeType("nt:query")) queries.add(manager.getQuery(node));
}
return queries;
}
/**
* Get node by giving the node user and the relative path to its
* @param userHome Node user
* @param relativePath The relative path to its
* @return
* @throws Exception
*/
private Node getNodeByRelativePath(Node userHome, String relativePath) throws Exception {
String[] paths = relativePath.split("/");
StringBuffer relPath = null;
Node queriesHome = null;
for (String path : paths) {
if (relPath == null)
relPath = new StringBuffer(path);
else
relPath.append("/").append(path);
if (!userHome.hasNode(relPath.toString()))
queriesHome = userHome.addNode(relPath.toString());
}
return queriesHome;
}
/**
* Get all permission of the giving owner
* @param owner
* @return
*/
private Map<String,String[]> getPermissions(String owner) {
Map<String, String[]> permissions = new HashMap<String, String[]>();
permissions.put(owner, perms);
permissions.put(group_, perms);
return permissions;
}
/**
* {@inheritDoc}
*/
public void addQuery(String queryName, String statement, String language, String userName) throws Exception {
if (userName == null)
return;
Session session = getSession();
QueryManager manager = session.getWorkspace().getQueryManager();
Query query = manager.createQuery(statement, language);
SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
Node userNode = nodeHierarchyCreator_.getUserNode(sessionProvider, userName);
if (!userNode.hasNode(getRelativePath())) {
getNodeByRelativePath(userNode, relativePath_);
session.save();
}
String absPath = userNode.getPath() + "/" + relativePath_ + "/" + queryName;
query.storeAsNode(absPath);
session.save();
}
/**
* {@inheritDoc}
*/
public void removeQuery(String queryPath, String userName) throws Exception {
if (userName == null)
return;
Session session = getSession();
Node queryNode = null;
try {
queryNode = (Node) session.getItem(queryPath);
} catch (PathNotFoundException pe) {
queryNode = (Node) getSession(WCMCoreUtils.getSystemSessionProvider(), true).getItem(queryPath);
}
Node queriesHome = queryNode.getParent();
queryNode.remove();
queriesHome.save();
removeFromCache(queryPath);
}
/**
* {@inheritDoc}
*/
public void addSharedQuery(String queryName,
String statement,
String language,
String[] permissions,
boolean cachedResult) throws Exception {
addSharedQuery(queryName,
statement,
language,
permissions,
cachedResult,
WCMCoreUtils.getUserSessionProvider());
}
public void addSharedQuery(String queryName,
String statement,
String language,
String[] permissions,
boolean cachedResult,
SessionProvider provider) throws Exception {
Session session = getSession(provider, true);
ValueFactory vt = session.getValueFactory();
String queryPath;
List<Value> perm = new ArrayList<Value>();
for (String permission : permissions) {
Value vl = vt.createValue(permission);
perm.add(vl);
}
Value[] vls = perm.toArray(new Value[] {});
String queriesPath = baseQueriesPath_;
Node queryHome = (Node)session.getItem(baseQueriesPath_);
QueryManager queryManager = session.getWorkspace().getQueryManager();
queryManager.createQuery(statement, language);
if (queryHome.hasNode(queryName)) {
Node query = queryHome.getNode(queryName);
query.setProperty("jcr:language", language);
query.setProperty("jcr:statement", statement);
query.setProperty("exo:accessPermissions", vls);
query.setProperty("exo:cachedResult", cachedResult);
query.save();
session.save();
queryPath = query.getPath();
} else {
QueryManager manager = session.getWorkspace().getQueryManager();
Query query = manager.createQuery(statement, language);
Node newQuery = query.storeAsNode(baseQueriesPath_ + "/" + queryName);
newQuery.addMixin("mix:sharedQuery");
newQuery.setProperty("exo:accessPermissions", vls);
newQuery.setProperty("exo:cachedResult", cachedResult);
session.getItem(queriesPath).save();
queryPath = queriesPath;
}
removeFromCache(queryPath);
}
/**
* {@inheritDoc}
*/
public Node getSharedQuery(String queryName, SessionProvider provider) throws Exception {
Session session = getSession(provider, true);
try {
Node sharedQueryNode = (Node) session.getItem(baseQueriesPath_ + "/" + queryName);
return sharedQueryNode;
} catch (PathNotFoundException e) {
return null;
}
}
/**
* {@inheritDoc}
*/
public List<Node> getSharedQueries(SessionProvider provider) throws Exception {
Session session = getSession(provider, true);
List<Node> queries = new ArrayList<Node>();
Node sharedQueryHome = (Node) session.getItem(baseQueriesPath_);
NodeIterator iter = sharedQueryHome.getNodes();
while (iter.hasNext()) {
Node node = iter.nextNode();
if(node.isNodeType("nt:query")) {
queries.add(node);
}
}
return queries;
}
/**
* {@inheritDoc}
*/
public List<Node> getSharedQueries(String userId, SessionProvider provider) throws Exception {
List<Node> sharedQueries = new ArrayList<Node>();
for(Node query : getSharedQueries(provider)) {
if (canUseQuery(userId, query)) {
sharedQueries.add(query);
}
}
return sharedQueries;
}
/**
* {@inheritDoc}
*/
public List<Node> getSharedQueries(String queryType,
String userId,
SessionProvider provider) throws Exception {
List<Node> resultList = new ArrayList<Node>();
String language = null;
for (Node queryNode: getSharedQueries(provider)) {
language = queryNode.getProperty("jcr:language").getString();
if (!queryType.equalsIgnoreCase(language)) continue;
if (canUseQuery(userId,queryNode)) {
resultList.add(queryNode);
}
}
return resultList;
}
/**
* {@inheritDoc}
*/
public Query getQueryByPath(String queryPath, String userName, SessionProvider provider) throws Exception {
List<Query> queries = getQueries(userName, provider);
for (Query query : queries) {
if (query.getStoredQueryPath().equals(queryPath)) return query;
}
return null;
}
/**
* {@inheritDoc}
*/
public void removeSharedQuery(String queryName, SessionProvider provider) throws Exception {
Session session = getSession(provider, true);
session.getItem(baseQueriesPath_ + "/" + queryName).remove();
session.save();
}
/**
* {@inheritDoc}
*/
public QueryResult execute(String queryPath,
String workspace,
SessionProvider provider,
String userId) throws Exception {
Session session = getSession(provider, true);
Session querySession = getSession(workspace, provider);
Node queryNode = null;
try {
queryNode = (Node) session.getItem(queryPath);
} catch (PathNotFoundException e) {
if (LOG.isWarnEnabled()) {
LOG.warn("Can not find node by path " + queryPath + " in dms-system workspace");
}
queryNode = (Node) querySession.getItem(queryPath);
}
if (queryNode != null && queryNode.hasProperty("exo:cachedResult")
&& queryNode.getProperty("exo:cachedResult").getBoolean()) {
String portalName = containerInfo_.getContainerName();
String key = portalName + queryPath;
QueryResult result = cache_.get(key);
if (result != null) return result;
result = execute(querySession, queryNode, userId);
cache_.put(key, result);
return result;
}
QueryResult queryResult = execute(querySession, queryNode, userId);
return queryResult;
}
/**
* Execute the query by giving the session, query node and userid
* @param session The Session
* @param queryNode The node of query
* @param userId The userid
* @return
* @throws Exception
*/
private QueryResult execute(Session session, Node queryNode, String userId) throws Exception {
return createQuery(session, queryNode, userId).execute();
}
/**
* This method replaces tokens in the statement by their actual values
* Current supported tokens are :
* ${UserId}$ corresponds to the current user
* ${Date}$ corresponds to the current date
* That way, predefined queries can be equipped with dynamic values. This is
* useful when querying for documents made by the current user, or documents
* in publication state.
*
* @return the processed String, with replaced tokens
*/
private String computeStatement(String statement, String userId) {
// The returned computed statement
String ret = statement;
// Replace ${UserId}$
ret = ret.replace("${UserId}$",userId);
// Replace ${Date}$
String currentDate = ISO8601.format(new GregorianCalendar());
ret = ret.replace("${Date}$",currentDate);
return ret;
}
/**
* Remove query from cache by giving the query path
* @param queryPath The path to query
* @throws Exception
*/
private void removeFromCache(String queryPath) throws Exception {
String portalName = containerInfo_.getContainerName();
String key = portalName + queryPath;
QueryResult result = cache_.get(key);
if (result != null) cache_.remove(key);
}
/**
* Get the session with curent repository
* @return
* @throws Exception
*/
private Session getSession() throws Exception {
ManageableRepository manageableRepository = repositoryService_.getCurrentRepository();
SessionProvider sessionProvider = WCMCoreUtils.getSystemSessionProvider();
return sessionProvider.getSession(manageableRepository.getConfiguration().getDefaultWorkspaceName(),
manageableRepository);
}
/**
* Get the session by specify the repository, sessionprovider and flag params
* @param provider The SessionProvider
* @param flag The boolean to decide which session will be chosen
* @return
* @throws Exception
*/
private Session getSession(SessionProvider provider, boolean flag) throws Exception {
ManageableRepository manageableRepository = repositoryService_.getCurrentRepository();
if (!flag) {
String workspace = manageableRepository.getConfiguration().getDefaultWorkspaceName();
return provider.getSession(workspace, manageableRepository);
}
DMSRepositoryConfiguration dmsRepoConfig = dmsConfiguration_.getConfig();
return provider.getSession(dmsRepoConfig.getSystemWorkspace(), manageableRepository);
}
/**
* Get the session by specify the repository, workspace and sessionprovider
* @param workspace The workspace name
* @param provider The SessionProvider
* @return
* @throws Exception
*/
private Session getSession(String workspace, SessionProvider provider) throws Exception {
ManageableRepository manageableRepository = repositoryService_.getCurrentRepository();
return provider.getSession(workspace,manageableRepository);
}
/**
* Check the given user can use this query
* @param userId The user id
* @param queryNode The node of query
* @return
* @throws Exception
*/
private boolean canUseQuery(String userId, Node queryNode) throws Exception{
Value[] values = queryNode.getProperty("exo:accessPermissions").getValues();
for(Value value : values) {
String accessPermission = value.getString();
if (hasMembership(userId,accessPermission)) {
return true;
}
}
return false;
}
/**
* Check the user which has a given membership
* @param userId The user id
* @param roleExpression The expression of membership
* @return
*/
private boolean hasMembership(String userId, String roleExpression) {
if (userId == null || userId.length() == 0) {
return false;
}
if(roleExpression.equals("*") || roleExpression.equals(IdentityConstants.ANY))
return true;
ConversationState conversationState = ConversationState.getCurrent();
Identity identity = conversationState.getIdentity();
String membershipType = roleExpression.substring(0, roleExpression.indexOf(":"));
String groupName = roleExpression.substring(roleExpression.indexOf(":") + 1);
try {
MembershipHandler membershipHandler = organizationService_.getMembershipHandler();
if ("*".equals(membershipType)) {
// Determine if there exists at least one membership
if (userId.equals(ConversationState.getCurrent().getIdentity().getUserId())) {
return identity.isMemberOf(groupName);
} else {
return !membershipHandler.findMembershipsByUserAndGroup( userId,groupName).isEmpty();
}
}
if (userId.equals(ConversationState.getCurrent().getIdentity().getUserId())) {
return identity.isMemberOf(groupName, membershipType);
} else {
// Determine if there exists the membership of specified type
return membershipHandler.findMembershipByUserGroupAndType(userId,groupName,membershipType) != null;
}
}
catch(Exception e) {
return false;
}
}
/**
* {@inheritDoc}
*/
public Query getQuery(String queryPath, String workspace, SessionProvider provider, String userId) throws Exception {
Session session = getSession(provider, true);
Session querySession = getSession(workspace, provider);
Node queryNode = null;
try {
queryNode = (Node) session.getItem(queryPath);
} catch (PathNotFoundException e) {
if (LOG.isWarnEnabled()) {
LOG.warn("Can not find node by path " + queryPath + " in dms-system workspace");
}
queryNode = (Node) querySession.getItem(queryPath);
}
return createQuery(querySession, queryNode, userId);
}
/**
* Creates the Query object by giving the session, query node and userid
* @param session The Session
* @param queryNode The node of query
* @param userId The userid
* @return
* @throws Exception
*/
private Query createQuery(Session session, Node queryNode, String userId) throws Exception {
String statement = this.computeStatement(queryNode.getProperty("jcr:statement").getString(), userId);
String language = queryNode.getProperty("jcr:language").getString();
Query query = session.getWorkspace().getQueryManager().createQuery(statement,language);
return query;
}
@Override
public Set<String> getAllConfiguredQueries() {
return configuredQueries_;
}
}