/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
*/
package org.olat.search.service.indexer.repository;
import java.io.IOException;
import java.util.List;
import org.apache.lucene.document.Document;
import org.olat.core.CoreSpringFactory;
import org.olat.core.commons.persistence.DB;
import org.olat.core.id.Identity;
import org.olat.core.id.OLATResourceable;
import org.olat.core.id.Roles;
import org.olat.core.id.context.BusinessControl;
import org.olat.core.id.context.ContextEntry;
import org.olat.core.logging.AssertException;
import org.olat.core.util.resource.OresHelper;
import org.olat.repository.RepositoryEntry;
import org.olat.repository.RepositoryManager;
import org.olat.repository.manager.RepositoryEntryDocumentFactory;
import org.olat.repository.model.SearchRepositoryEntryParameters;
import org.olat.resource.accesscontrol.ACService;
import org.olat.resource.accesscontrol.AccessResult;
import org.olat.resource.accesscontrol.OfferAccess;
import org.olat.resource.accesscontrol.provider.free.FreeAccessHandler;
import org.olat.resource.accesscontrol.provider.paypal.PaypalAccessHandler;
import org.olat.search.SearchModule;
import org.olat.search.service.SearchResourceContext;
import org.olat.search.service.indexer.AbstractHierarchicalIndexer;
import org.olat.search.service.indexer.Indexer;
import org.olat.search.service.indexer.OlatFullIndexer;
/**
* Index the whole OLAT-repository.
* @author Christian Guretzki
*
*/
public class RepositoryIndexer extends AbstractHierarchicalIndexer {
private static final int BATCH_SIZE = 100;
private DB dbInstance;
private RepositoryManager repositoryManager;
private RepositoryEntryDocumentFactory documentFactory;
private List<Long> repositoryBlackList;
private RepositoryIndexer() {
//
}
/**
* [used by spring]
*/
public void setSearchModule(SearchModule searchModule) {
repositoryBlackList = searchModule.getRepositoryBlackList();
}
/**
* [used by spring]
*/
public void setRepositoryEntryDocumentFactory(RepositoryEntryDocumentFactory documentFactory) {
this.documentFactory = documentFactory;
}
/**
* [used by spring]
*/
public void setRepositoryManager(RepositoryManager repositoryManager) {
this.repositoryManager = repositoryManager;
}
/**
* [used by spring]
*/
public void setDbInstance(DB dbInstance) {
this.dbInstance = dbInstance;
}
/**
* Loops over all repository-entries. Index repository meta data.
* Go further with repository-indexer for certain type if available.
* @see org.olat.search.service.indexer.Indexer#doIndex(org.olat.search.service.SearchResourceContext, java.lang.Object, org.olat.search.service.indexer.OlatFullIndexer)
*/
@Override
public void doIndex(SearchResourceContext parentResourceContext, Object businessObj, OlatFullIndexer indexWriter) throws IOException,InterruptedException {
final Roles roles = new Roles(true, true, true, true, false, true, false);
final SearchRepositoryEntryParameters params = new SearchRepositoryEntryParameters();
params.setRoles(roles);
boolean debug = isLogDebugEnabled();
// loop over all repository-entries
// committing here to make sure the loadBusinessGroup below does actually
// reload from the database and not only use the session cache
// (see org.hibernate.Session.get():
// If the instance, or a proxy for the instance, is already associated with the session, return that instance or proxy.)
dbInstance.commitAndCloseSession();
int counter = 0;
List<RepositoryEntry> repositoryList;
do {
repositoryList = repositoryManager.genericANDQueryWithRolesRestriction(params, counter, BATCH_SIZE, true);
for(RepositoryEntry repositoryEntry:repositoryList) {
try {
// reload the repositoryEntry here before indexing it to make sure it has not been deleted in the meantime
RepositoryEntry reloadedRepositoryEntry = repositoryManager.lookupRepositoryEntry(repositoryEntry.getKey());
if (reloadedRepositoryEntry==null) {
logInfo("doIndex: repositoryEntry was deleted while we were indexing. The deleted repositoryEntry was: "+repositoryEntry);
continue;
}
if(repositoryEntry.getAccess() == RepositoryEntry.DELETED) {
continue;
}
repositoryEntry = reloadedRepositoryEntry;
if (debug) {
logDebug("Index repositoryEntry=" + repositoryEntry + " counter=" + counter++ + " with ResourceableId=" + repositoryEntry.getOlatResource().getResourceableId());
}
if (!isOnBlacklist(repositoryEntry.getOlatResource().getResourceableId()) ) {
SearchResourceContext searchResourceContext = new SearchResourceContext(parentResourceContext);
searchResourceContext.setBusinessControlFor(repositoryEntry);
searchResourceContext.setTitle(repositoryEntry.getDisplayname());
searchResourceContext.setDescription(repositoryEntry.getDescription());
Document document = documentFactory.createDocument(searchResourceContext, repositoryEntry);
indexWriter.addDocument(document);
// Pass created-date & modified-date in context to child indexer because the child have no dates
searchResourceContext.setLastModified(repositoryEntry.getLastModified());
searchResourceContext.setCreatedDate(repositoryEntry.getCreationDate());
// go further with resource
Indexer repositoryEntryIndexer = getRepositoryEntryIndexer(repositoryEntry);
if (repositoryEntryIndexer != null) {
repositoryEntryIndexer.doIndex(searchResourceContext, repositoryEntry, indexWriter);
} else if (debug) {
logDebug("No RepositoryEntryIndexer for " + repositoryEntry.getOlatResource()); // e.g. RepositoryEntry
}
} else {
logWarn("RepositoryEntry is on black-list and excluded from search-index, repositoryEntry=" + repositoryEntry, null);
}
} catch (Throwable ex) {
// create meaninfull debugging output to find repo entry that is somehow broken
String entryDebug = "NULL";
if (repositoryEntry != null) {
entryDebug = "resId::" + repositoryEntry.getResourceableId() + " resTypeName::" + repositoryEntry.getResourceableTypeName() + " resName::" + repositoryEntry.getResourcename();
}
logWarn("Exception=" + ex.getMessage() + " for repo entry " + entryDebug, ex);
dbInstance.rollbackAndCloseSession();
}
dbInstance.commitAndCloseSession();
}
counter += repositoryList.size();
} while(repositoryList.size() == BATCH_SIZE);
if (debug) {
logDebug("RepositoryIndexer finished. counter=" + counter);
}
}
private boolean isOnBlacklist(Long key) {
return repositoryBlackList.contains(key);
}
/**
*
* @see org.olat.search.service.indexer.Indexer#getSupportedTypeName()
*/
public String getSupportedTypeName() {
return OresHelper.calculateTypeName(RepositoryEntry.class);
}
/**
*
* @see org.olat.search.service.indexer.Indexer#checkAccess(org.olat.core.id.context.ContextEntry, org.olat.core.id.context.BusinessControl, org.olat.core.id.Identity, org.olat.core.id.Roles)
*/
@Override
public boolean checkAccess(ContextEntry contextEntry, BusinessControl businessControl, Identity identity, Roles roles) {
boolean debug = isLogDebugEnabled();
if (debug) logDebug("checkAccess for businessControl=" + businessControl + " identity=" + identity + " roles=" + roles);
Long repositoryKey = contextEntry.getOLATResourceable().getResourceableId();
RepositoryEntry repositoryEntry = repositoryManager.lookupRepositoryEntry(repositoryKey);
if (repositoryEntry != null) {
boolean isOwner = repositoryManager.isOwnerOfRepositoryEntry(identity,repositoryEntry);
boolean isAllowedToLaunch = false;
if (!isOwner) {
isAllowedToLaunch = repositoryManager.isAllowedToLaunch(identity, roles, repositoryEntry);
if(isAllowedToLaunch) {
List<ContextEntry> entries = businessControl.getEntriesDownTheControls();
if(entries.size() > 1) {
boolean hasAccess = false;
ACService acService = CoreSpringFactory.getImpl(ACService.class);
AccessResult acResult = acService.isAccessible(repositoryEntry, identity, false);
if (acResult.isAccessible()) {
hasAccess = true;
} else if (!acResult.getAvailableMethods().isEmpty()) {
for(OfferAccess offer:acResult.getAvailableMethods()) {
String type = offer.getMethod().getType();
if (type.equals(FreeAccessHandler.METHOD_TYPE) || type.equals(PaypalAccessHandler.METHOD_TYPE)) {
hasAccess = true;
}
}
}
isAllowedToLaunch = hasAccess;
}
}
}
if (debug) logDebug("isOwner=" + isOwner + " isAllowedToLaunch=" + isAllowedToLaunch);
if (isOwner || isAllowedToLaunch) {
Indexer repositoryEntryIndexer = getRepositoryEntryIndexer(repositoryEntry);
if (debug) logDebug("repositoryEntryIndexer=" + repositoryEntryIndexer);
if (repositoryEntryIndexer != null) {
return super.checkAccess(contextEntry, businessControl, identity, roles)
&& repositoryEntryIndexer.checkAccess(contextEntry, businessControl, identity, roles);
} else {
// No Indexer => no access
return false;
}
} else {
return false;
}
} else {
logWarn("Can not found RepositoryEntry with key=" + repositoryKey, null);
return false;
}
}
/**
* Get the repository handler for this repository entry.
* @param re
* @return the handler or null if no appropriate handler could be found
*/
public Indexer getRepositoryEntryIndexer(RepositoryEntry re) {
OLATResourceable ores = re.getOlatResource();
if (ores == null) throw new AssertException("RepositoryEntry has no OlatResource [re.getOlatResource()==null].");
return getRepositoryEntryIndexer(ores.getResourceableTypeName());
}
/**
* Get a repository handler which supports the given resourceable type.
* @param resourceableTypeName
* @return the handler or null if no appropriate handler could be found
*/
public Indexer getRepositoryEntryIndexer(String resourceableTypeName) {
List<Indexer> indexers = getIndexerByType(resourceableTypeName);
if(indexers != null && !indexers.isEmpty()) {
return indexers.get(0);
}
return null;
}
}