001/* 002 * Copyright 2020 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.odf.schedulable; 017 018import java.io.IOException; 019import java.util.Collections; 020import java.util.Set; 021import java.util.stream.Collectors; 022 023import org.apache.avalon.framework.service.ServiceException; 024import org.apache.avalon.framework.service.ServiceManager; 025import org.apache.cocoon.components.ContextHelper; 026import org.apache.cocoon.environment.Request; 027import org.apache.commons.lang3.StringUtils; 028import org.apache.solr.client.solrj.SolrClient; 029import org.apache.solr.client.solrj.SolrServerException; 030import org.quartz.JobDataMap; 031import org.quartz.JobExecutionContext; 032 033import org.ametys.cms.content.indexing.solr.SolrIndexer; 034import org.ametys.cms.repository.Content; 035import org.ametys.cms.search.content.ContentSearcherFactory; 036import org.ametys.cms.search.query.Query.Operator; 037import org.ametys.cms.search.query.StringQuery; 038import org.ametys.cms.search.solr.SolrClientProvider; 039import org.ametys.odf.ProgramItem; 040import org.ametys.odf.catalog.CatalogsManager; 041import org.ametys.odf.program.ProgramFactory; 042import org.ametys.odf.program.SubProgramFactory; 043import org.ametys.plugins.core.impl.schedule.AbstractStaticSchedulable; 044import org.ametys.plugins.core.schedule.Scheduler; 045import org.ametys.plugins.repository.RepositoryConstants; 046import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector; 047 048/** 049 * Scheduler to compute the acquired skills on subprogram/programs from skills hold by their courses. 050 */ 051public class ComputeProgramSkillsSchedulable extends AbstractStaticSchedulable 052{ 053 /** The key for the catalog */ 054 public static final String JOBDATAMAP_CATALOG_KEY = "catalog"; 055 056 /** The catalog manager */ 057 protected CatalogsManager _catalogsManager; 058 /** The solr indexer */ 059 protected SolrIndexer _solrIndexer; 060 /** The provider for solr client */ 061 protected SolrClientProvider _solrClientProvider; 062 /** The content searcher */ 063 protected ContentSearcherFactory _contentSearcherFactory; 064 065 @Override 066 public void service(ServiceManager manager) throws ServiceException 067 { 068 super.service(manager); 069 _catalogsManager = (CatalogsManager) manager.lookup(CatalogsManager.ROLE); 070 _solrIndexer = (SolrIndexer) manager.lookup(SolrIndexer.ROLE); 071 _solrClientProvider = (SolrClientProvider) manager.lookup(SolrClientProvider.ROLE); 072 _contentSearcherFactory = (ContentSearcherFactory) manager.lookup(ContentSearcherFactory.ROLE); 073 } 074 075 @Override 076 public void execute(JobExecutionContext context) throws Exception 077 { 078 doReindex(context, RepositoryConstants.DEFAULT_WORKSPACE); 079 } 080 081 /** 082 * Find and reindex contents for a given workspace and catalog 083 * @param context The execution context 084 * @param workspaceName the workspace's name 085 */ 086 protected void doReindex(JobExecutionContext context, String workspaceName) 087 { 088 long time_0 = System.currentTimeMillis(); 089 090 JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); 091 String catalogName = jobDataMap.getString(Scheduler.PARAM_VALUES_PREFIX + JOBDATAMAP_CATALOG_KEY); 092 093 Set<String> contentIds = getContentIds(catalogName, workspaceName); 094 indexContents(contentIds, workspaceName); 095 096 getLogger().debug("Successfully reindex {} programs and subprograms for workspace '{}' and catalog '{}' in {} ms", contentIds.size(), workspaceName, catalogName, System.currentTimeMillis() - time_0); 097 } 098 099 /** 100 * Get the id of contents to re-index 101 * @param catalogName The catalog's name. Can be empty or null to get contents of default catalog 102 * @param workspaceName the workspace's name 103 * @return the id of contents to re-index 104 */ 105 protected Set<String> getContentIds(String catalogName, String workspaceName) 106 { 107 Request request = ContextHelper.getRequest(_context); 108 String currentWorkspace = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 109 110 try 111 { 112 // Force workspace 113 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 114 115 String searchCatalogName = catalogName; 116 if (StringUtils.isEmpty(searchCatalogName)) 117 { 118 searchCatalogName = _catalogsManager.getDefaultCatalogName(); 119 } 120 121 Set<String> contentIds = _contentSearcherFactory 122 .create(ProgramFactory.PROGRAM_CONTENT_TYPE, SubProgramFactory.SUBPROGRAM_CONTENT_TYPE) 123 .setCheckRights(false) 124 .search(new StringQuery(ProgramItem.CATALOG, Operator.EQ, searchCatalogName, null)) 125 .stream() 126 .map(Content::getId) 127 .collect(Collectors.toSet()); 128 129 getLogger().debug("Found {} contents to reindex for workspace '{}' and catalog '{}'", contentIds.size(), workspaceName, searchCatalogName); 130 131 return contentIds; 132 } 133 catch (Exception e) 134 { 135 getLogger().error("Unable to get content ids to reindex for updating skills", e); 136 return Collections.EMPTY_SET; 137 } 138 finally 139 { 140 // Restore current workspace 141 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWorkspace); 142 } 143 } 144 145 /** 146 * Index the given contents for a given workspace 147 * @param contentIds the id of contents to index 148 * @param workspaceName the workspace's name 149 */ 150 protected void indexContents(Set<String> contentIds, String workspaceName) 151 { 152 SolrClient solrClient = _solrClientProvider.getUpdateClient(workspaceName, false); 153 154 contentIds.stream().forEach(id -> 155 { 156 try 157 { 158 _solrIndexer.indexContent(id, workspaceName, false, solrClient); 159 } 160 catch (Exception e) 161 { 162 getLogger().error("Fail to re-index content with id '{}' after computing skills", id); 163 } 164 }); 165 166 try 167 { 168 // Commit all uncommited changes 169 _solrIndexer.commit(); 170 } 171 catch (IOException | SolrServerException e) 172 { 173 getLogger().error("Unable to commit changes into Solr index after computing skills", e); 174 } 175 } 176}