/*
* Copyright (c) 2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* 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 org.wso2.carbon.registry.indexing;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrException;
import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.registry.core.*;
import org.wso2.carbon.registry.core.config.Mount;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import org.wso2.carbon.registry.core.jdbc.handlers.Handler;
import org.wso2.carbon.registry.core.jdbc.handlers.RequestContext;
import org.wso2.carbon.registry.core.session.CurrentSession;
import org.wso2.carbon.registry.core.session.UserRegistry;
import org.wso2.carbon.registry.core.utils.MediaTypesUtils;
import org.wso2.carbon.registry.core.utils.RegistryUtils;
import org.wso2.carbon.registry.extensions.utils.CommonUtil;
import org.wso2.carbon.registry.indexing.AsyncIndexer.File2Index;
import org.wso2.carbon.registry.indexing.indexer.IndexDocumentCreator;
import org.wso2.carbon.registry.indexing.indexer.IndexerException;
import org.wso2.carbon.registry.indexing.solr.SolrClient;
import org.wso2.carbon.registry.indexing.utils.IndexingUtils;
import org.wso2.carbon.user.core.UserRealm;
import org.wso2.carbon.user.core.UserStoreException;
import org.wso2.carbon.utils.ServerConstants;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
/**
* A handler that performs indexing of resources using a Apache Solr server, and then provides
* results for content searches based on the indexes. A request for indexing is submitted whenever a
* resource is added, moved, copied or renamed. Created indexes are cleaned up when resources
* renamed, moved or deleted.
* <p/>
* Indexing is an asynchronous operations, and only the resources having a media type for which an
* indexer is registered will be indexed. Indexing will not cause an impact on generic registry
* operations, and is design to work as a background operation.
*/
public class IndexingHandler extends Handler {
private static Log log = LogFactory.getLog(IndexingHandler.class);
private volatile static AsyncIndexer asyncIndexer;
/**
* <property name="indexingUrl" type="xml" value="url"/>
*/
public void put(RequestContext requestContext) throws RegistryException {
if (log.isDebugEnabled()){
log.debug(" Before put resources into indexer " + requestContext.getResourcePath().getPath());
}
if (isIndexablePutOperation(requestContext) || Utils.getRegistryService() == null) {
return;
}
PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext();
String path = getRegistryPath(requestContext);
submitFileForIndexing(getIndexer(), requestContext.getResource(), path,
null,carbonContext.getTenantId(), carbonContext.getTenantDomain() );
if (log.isDebugEnabled()){
log.debug(" After put resources into indexer "+ requestContext.getResourcePath().getPath());
}
}
@Override
public String move(RequestContext requestContext) throws RegistryException {
if (isExecutingMountedHandlerChain(requestContext)) {
return super.move(requestContext);
}
String oldPath = requestContext.getSourcePath();
String newPath = requestContext.getTargetPath();
int tenantId = CurrentSession.getTenantId();
try {
deleteFromIndex(oldPath, tenantId);
} catch (SolrException e) {
log.error("Could not delete file for Solr server", e);
} catch (RegistryException e) {
log.error("Could not delete file for Solr server", e);
}
Resource resource = requestContext.getRegistry().get(oldPath);
PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext();
String path = CommonUtil.getRegistryPath(requestContext.getRegistry().getRegistryContext(),newPath);
submitFileForIndexing(getIndexer(), resource, path, null, carbonContext.getTenantId(),
carbonContext.getTenantDomain());
return super.move(requestContext);
}
private void deleteFromIndex(String oldPath, int tenantId) throws RegistryException {
getIndexer().getClient().deleteFromIndex(oldPath, tenantId);
}
@Override
public String rename(RequestContext requestContext) throws RegistryException {
if (isExecutingMountedHandlerChain(requestContext)) {
return super.rename(requestContext);
}
String oldPath = requestContext.getSourcePath();
String newPath = requestContext.getTargetPath();
int tenantId = CurrentSession.getTenantId();
try {
deleteFromIndex(oldPath, tenantId);
} catch (SolrException e) {
log.error("Could not delete file for Solr server", e);
} catch (RegistryException e) {
log.error("Could not delete file for Solr server", e);
}
Resource resource = requestContext.getRegistry().get(oldPath);
PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext();
String path = CommonUtil.getRegistryPath(requestContext.getRegistry().getRegistryContext(),newPath);
submitFileForIndexing(getIndexer(), resource, path, null, carbonContext.getTenantId(),
carbonContext.getTenantDomain());
return super.rename(requestContext);
}
@Override
public String copy(RequestContext requestContext) throws RegistryException {
if (isExecutingMountedHandlerChain(requestContext)) {
return super.rename(requestContext);
}
String oldPath = requestContext.getSourcePath();
String newPath = requestContext.getTargetPath();
Resource resource = requestContext.getRegistry().get(oldPath);
PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext();
String path = CommonUtil.getRegistryPath(requestContext.getRegistry().getRegistryContext(),newPath);
submitFileForIndexing(getIndexer(), resource, path, null, carbonContext.getTenantId(),
carbonContext.getTenantDomain());
return super.copy(requestContext);
}
private boolean isIndexable(RequestContext requestContext) {
return isExecutingMountedHandlerChain(requestContext)
|| requestContext.getResource() == null
|| requestContext.getResource().getMediaType() == null
|| requestContext.getResource() instanceof Collection;
}
private boolean isIndexablePutOperation(RequestContext requestContext) {
return (requestContext.getRegistry().getRegistryContext() == null)
|| requestContext.getResource() == null
|| requestContext.getResource().getMediaType() == null
|| requestContext.getResource() instanceof Collection;
}
private boolean isExecutingMountedHandlerChain(RequestContext requestContext) {
return (requestContext.getRegistry().getRegistryContext() == null)
|| requestContext.getRegistry().getRegistryContext().isClone();
}
@Override
public Collection searchContent(RequestContext requestContext) throws RegistryException {
String searchQuery = requestContext.getKeywords();
UserRegistry registry = CurrentSession.getUserRegistry();
SolrClient client;
List<String> filteredResults = new ArrayList<String>();
try {
client = SolrClient.getInstance();
SolrDocumentList results = client.query(searchQuery, CurrentSession.getTenantId());
if (log.isDebugEnabled()){
log.debug("result received "+ results);
}
for(int i = 0;i < results.getNumFound();i++){
SolrDocument solrDocument = results.get(i);
String path = getPathFromId((String)solrDocument.getFirstValue("id"));
//if (AuthorizationUtils.authorize(path, ActionConstants.GET)){
if(isAuthorized(registry,path, ActionConstants.GET)){
filteredResults.add(path);
}
}
if (log.isDebugEnabled()) {
log.debug("filtered results "+ filteredResults + " for user "+ registry.getUserName());
}
} catch (IndexerException e) {
log.error("Unable to do Content Search", e);
}
String[] resourcePaths = filteredResults.toArray(new String[filteredResults.size()]);
Collection searchResults = new CollectionImpl();
searchResults.setContent(resourcePaths);
return searchResults;
}
private String getPathFromId(String id) {
return id.substring(0, id.lastIndexOf(IndexingConstants.FIELD_TENANT_ID));
}
private boolean isAuthorized(UserRegistry registry, String resourcePath, String action) throws RegistryException {
UserRealm userRealm = registry.getUserRealm();
String userName = getLoggedInUserName();
try {
if (!userRealm.getAuthorizationManager().isUserAuthorized(userName,
resourcePath, action)) {
return false;
}
} catch (UserStoreException e) {
throw new RegistryException("Error while authorizing " + resourcePath
+ " with user " + userName + ":" + e.getMessage(), e);
}
return true;
}
private String getLoggedInUserName() {
return PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername();
}
public void importResource(RequestContext requestContext) throws RegistryException {
if (isIndexable(requestContext)) {
return;
}
PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext();
String path = getRegistryPath(requestContext);
submitFileForIndexing(getIndexer(), requestContext.getResource(), path, requestContext.getSourceURL(),
carbonContext.getTenantId(), carbonContext.getTenantDomain());
}
public void delete(RequestContext requestContext) throws RegistryException {
final String id = requestContext.getResourcePath().getPath();
final int tenantId = CurrentSession.getTenantId();
new Thread(new Runnable() {
int tid = tenantId;
public void run() {
try {
deleteFromIndex(id, tid);
} catch (SolrException e) {
log.error("Could not delete file for Solr server", e);
} catch (RegistryException e) {
log.error("Could not delete file for Solr server", e);
}
}
}).start();
}
public void putChild(RequestContext requestContext) throws RegistryException {
}
public void importChild(RequestContext requestContext) throws RegistryException {
}
private AsyncIndexer getIndexer() throws RegistryException {
try {
if (asyncIndexer == null) {
synchronized (this) {
if (asyncIndexer == null) {
asyncIndexer = null;//AsyncIndexer.getInstance();
asyncIndexer = new AsyncIndexer();
new Thread(asyncIndexer).run();
}
}
}
return asyncIndexer;
} catch (SolrException e) {
throw new RegistryException(e.getMessage(),e);
}
}
private void submitFileForIndexing(AsyncIndexer indexer, Resource resource, String path, String sourceURL,
int tenantId, String tenantDomain) {
//if media type is null, mostly it is not a file. We will skip.
String mediaType = resource.getMediaType();
if (mediaType == null && path != null) {
try {
mediaType = MediaTypesUtils.getMediaType(RegistryUtils.getResourceName(path));
} catch (RegistryException ignored) {
// We are only making an attempt to determine the media type.
}
}
if (mediaType == null || Utils.getRegistryService() == null ||
IndexingManager.getInstance().getIndexerForMediaType(mediaType) == null) {
return;
}
if (log.isDebugEnabled()) {
log.debug("Submitting file "+ path + " for Indexing");
}
try {
String lcName = resource.getProperty("registry.LC.name");
String lcState = lcName != null ? resource.getProperty("registry.lifecycle." + lcName + ".state") : null;
File2Index file2Index = new File2Index(IndexingUtils.getByteContent(resource, sourceURL), mediaType, path,
CurrentSession.getTenantId(), tenantDomain, lcName, lcState);
String resourcePath = file2Index.path;
Registry registry = IndexingManager.getInstance().getRegistry(file2Index.tenantId);
Resource resourceToIndex;
//Check whether resource exists before indexing the resource
if (resourcePath != null && registry.resourceExists(resourcePath) &&
(resourceToIndex = registry.get(resourcePath)) != null) {
// Create the IndexDocument
IndexDocumentCreator indexDocumentCreator = new IndexDocumentCreator(file2Index, resourceToIndex);
indexDocumentCreator.createIndexDocument();
// Here, we are checking whether a resource has a symlink associated to it, if so, we submit that symlink path
// in the indexer. see CARBON-11510.
String symlinkPath = resourceToIndex.getProperty("registry.resource.symlink.path");
if (symlinkPath != null) {
// Create the IndexDocument
file2Index.path = symlinkPath;
indexDocumentCreator = new IndexDocumentCreator(file2Index, resourceToIndex);
indexDocumentCreator.createIndexDocument();
}
}
} catch (RegistryException | IndexerException e) {
log.error("An error occurred while submitting file for indexing", e);
}
}
private String getRegistryPath(RequestContext requestContext) {
String servicePath = requestContext.getResourcePath().getPath();
List<Mount> mounts = requestContext.getRegistry().getRegistryContext().getMounts();
for (Mount mount: mounts) {
String mountPath = mount.getPath();
if (servicePath.startsWith(mount.getTargetPath())){
return servicePath.replace(mount.getTargetPath(), mountPath);
}
}
return servicePath;
}
}