001/* 002 * Copyright 2015 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.cms.content.indexing.solr; 017 018import java.io.IOException; 019import java.nio.ByteBuffer; 020import java.nio.CharBuffer; 021import java.nio.charset.CharsetDecoder; 022import java.nio.charset.CodingErrorAction; 023import java.nio.charset.StandardCharsets; 024import java.text.SimpleDateFormat; 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.Collection; 028import java.util.Collections; 029import java.util.Comparator; 030import java.util.HashMap; 031import java.util.HashSet; 032import java.util.List; 033import java.util.Map; 034import java.util.Objects; 035import java.util.Set; 036import java.util.concurrent.Future; 037import java.util.function.Function; 038import java.util.stream.Collectors; 039import java.util.stream.StreamSupport; 040 041import javax.jcr.RepositoryException; 042 043import org.apache.avalon.framework.activity.Initializable; 044import org.apache.avalon.framework.component.Component; 045import org.apache.avalon.framework.context.Context; 046import org.apache.avalon.framework.context.ContextException; 047import org.apache.avalon.framework.context.Contextualizable; 048import org.apache.avalon.framework.service.ServiceException; 049import org.apache.avalon.framework.service.ServiceManager; 050import org.apache.avalon.framework.service.Serviceable; 051import org.apache.cocoon.Constants; 052import org.apache.cocoon.components.ContextHelper; 053import org.apache.cocoon.environment.Request; 054import org.apache.commons.collections4.IterableUtils; 055import org.apache.commons.lang3.ObjectUtils; 056import org.apache.commons.lang3.StringUtils; 057import org.apache.solr.client.solrj.SolrClient; 058import org.apache.solr.client.solrj.SolrResponse; 059import org.apache.solr.client.solrj.SolrServerException; 060import org.apache.solr.client.solrj.request.CoreAdminRequest; 061import org.apache.solr.client.solrj.request.CoreAdminRequest.Create; 062import org.apache.solr.client.solrj.request.schema.FieldTypeDefinition; 063import org.apache.solr.client.solrj.request.schema.SchemaRequest; 064import org.apache.solr.client.solrj.request.schema.SchemaRequest.Update; 065import org.apache.solr.client.solrj.response.CoreAdminResponse; 066import org.apache.solr.client.solrj.response.SolrResponseBase; 067import org.apache.solr.client.solrj.response.UpdateResponse; 068import org.apache.solr.client.solrj.response.schema.SchemaRepresentation; 069import org.apache.solr.client.solrj.response.schema.SchemaResponse; 070import org.apache.solr.client.solrj.util.ClientUtils; 071import org.apache.solr.common.SolrInputDocument; 072import org.apache.solr.common.params.CoreAdminParams; 073import org.apache.solr.common.params.CoreAdminParams.CoreAdminAction; 074import org.apache.solr.common.params.ModifiableSolrParams; 075import org.apache.solr.common.params.SolrParams; 076import org.apache.solr.common.util.NamedList; 077import org.slf4j.Logger; 078 079import org.ametys.cms.indexing.IndexingException; 080import org.ametys.cms.indexing.solr.AbstractIndexerCallable; 081import org.ametys.cms.indexing.solr.IndexationResult; 082import org.ametys.cms.indexing.solr.ReloadAclCacheRequest; 083import org.ametys.cms.indexing.solr.ThreadIndexerHelper; 084import org.ametys.cms.indexing.solr.UpdateAclCacheRequest; 085import org.ametys.cms.indexing.solr.UpdateCorePropertyRequest; 086import org.ametys.cms.repository.Content; 087import org.ametys.cms.repository.ContentQueryHelper; 088import org.ametys.cms.repository.WorkflowAwareContent; 089import org.ametys.cms.rights.solrchecking.ReadAccessHelper; 090import org.ametys.cms.search.query.ContentAttachmentQuery; 091import org.ametys.cms.search.query.DocumentTypeQuery; 092import org.ametys.cms.search.query.OrQuery; 093import org.ametys.cms.search.query.Query; 094import org.ametys.cms.search.query.ResourceLocationQuery; 095import org.ametys.cms.search.solr.NoAutoCommitUpdateClient; 096import org.ametys.cms.search.solr.SolrClientProvider; 097import org.ametys.cms.search.solr.schema.SchemaDefinition; 098import org.ametys.cms.search.solr.schema.SchemaDefinitionProvider; 099import org.ametys.cms.search.solr.schema.SchemaDefinitionProviderExtensionPoint; 100import org.ametys.cms.search.solr.schema.SchemaFields; 101import org.ametys.cms.search.solr.schema.SchemaHelper; 102import org.ametys.cms.trash.element.DefaultTrashElement; 103import org.ametys.cms.trash.element.TrashElementFactory; 104import org.ametys.core.group.GroupIdentity; 105import org.ametys.core.right.AllowedUsers; 106import org.ametys.core.schedule.progression.ContainerProgressionTracker; 107import org.ametys.core.schedule.progression.ProgressionTrackerFactory; 108import org.ametys.core.schedule.progression.SimpleProgressionTracker; 109import org.ametys.core.user.UserIdentity; 110import org.ametys.core.util.DateUtils; 111import org.ametys.plugins.explorer.resources.Resource; 112import org.ametys.plugins.explorer.resources.ResourceCollection; 113import org.ametys.plugins.repository.AmetysObject; 114import org.ametys.plugins.repository.AmetysObjectIterable; 115import org.ametys.plugins.repository.AmetysObjectResolver; 116import org.ametys.plugins.repository.RepositoryConstants; 117import org.ametys.plugins.repository.TraversableAmetysObject; 118import org.ametys.plugins.repository.UnknownAmetysObjectException; 119import org.ametys.plugins.repository.collection.AmetysObjectCollection; 120import org.ametys.plugins.repository.provider.AbstractRepository; 121import org.ametys.plugins.repository.provider.JackrabbitRepository; 122import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector; 123import org.ametys.plugins.repository.provider.WorkspaceSelector; 124import org.ametys.plugins.repository.query.QueryHelper; 125import org.ametys.runtime.config.Config; 126import org.ametys.runtime.i18n.I18nizableText; 127import org.ametys.runtime.plugin.component.AbstractLogEnabled; 128 129/** 130 * Solr indexer. 131 */ 132public class SolrIndexer extends AbstractLogEnabled implements Component, Serviceable, Initializable, Contextualizable 133{ 134 /** The component role. */ 135 public static final String ROLE = SolrIndexer.class.getName(); 136 137 private static final ThreadLocal<SimpleDateFormat> __DATE_FORMAT = new ThreadLocal<>(); 138 139 private static final String _CONFIGSET_NAME_PREFIX = "configset-"; 140 141 private static final List<String> _READ_ONLY_FIELDS = Arrays.asList("id", "_version_", "_text_"); 142 private static final List<String> _READ_ONLY_FIELDTYPES = Arrays.asList("string", "plong", "text_general"); 143 144 private static final int __SOLR_STRING_NB_BYTES_LIMIT = 32766; 145 146 /** The service manager. */ 147 protected ServiceManager _manager; 148 /** The ametys object resolver. */ 149 protected AmetysObjectResolver _resolver; 150 /** The schema definition provider extension point. */ 151 protected SchemaDefinitionProviderExtensionPoint _schemaDefProviderEP; 152 /** The schema helper. */ 153 protected SchemaHelper _schemaHelper; 154 /** Solr Ametys contents indexer */ 155 protected SolrContentIndexer _solrContentIndexer; 156 /** Solr workflow indexer. */ 157 protected SolrWorkflowIndexer _solrWorkflowIndexer; 158 /** Solr resource indexer. */ 159 protected SolrResourceIndexer _solrResourceIndexer; 160 /** Solr trash element indexer. */ 161 protected SolrTrashElementIndexer _solrTrashElementIndexer; 162 163 /** The Solr client provider */ 164 protected SolrClientProvider _solrClientProvider; 165 166 /** The solr core prefix. */ 167 protected String _solrCorePrefix; 168 /** The Ametys internal URL used by Solr to query Ametys */ 169 protected String _ametysInternalUrl; 170 171 /** The workspace selector. */ 172 protected WorkspaceSelector _workspaceSelector; 173 /** The JCR repository */ 174 protected JackrabbitRepository _repository; 175 /** The helper for read access */ 176 protected ReadAccessHelper _readAccessHelper; 177 /** The thread indexer helper */ 178 protected ThreadIndexerHelper _threadIndexerHelper; 179 180 /** The avalon context */ 181 protected Context _context; 182 /** Cocoon Context */ 183 protected org.apache.cocoon.environment.Context _cocoonContext; 184 185 /** 186 * Returns the formatter for indexing dates. This is used for adding a dates as formatted strings (and not with date object directly) to prevent indexing of the wrong value because of time zone 187 * @return The date format for indexing dates 188 * @deprecated use {@link DateUtils#zonedDateTimeToString(java.time.ZonedDateTime, java.time.ZoneId)} using UTC zone id instead 189 */ 190 @Deprecated 191 public static SimpleDateFormat dateFormat() 192 { 193 if (__DATE_FORMAT.get() == null) 194 { 195 __DATE_FORMAT.set(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")); 196 } 197 return __DATE_FORMAT.get(); 198 } 199 200 /** 201 * Truncates (if needed) the given string in order to be indexed without <i>immense term</i> error by Solr. 202 * Only the {@value #__SOLR_STRING_NB_BYTES_LIMIT} first bytes of the String will be kept. 203 * @param value The string value to index 204 * @param logger The logger for logging in WARN level in case the given string is too long and will be truncated. Can be null if you do not want to log. 205 * @param documentId The id of the document being indexed. Can be null if you do not want to log. 206 * @param fieldName The name of the field being indexed. Can be null if you do not want to log. 207 * @return The given string value, or its truncation if it is too long (greater than {@value #__SOLR_STRING_NB_BYTES_LIMIT} bytes) 208 */ 209 public static String truncateUtf8StringValue(String value, Logger logger, String documentId , String fieldName) 210 { 211 if (value.length() * 4 <= __SOLR_STRING_NB_BYTES_LIMIT) 212 { 213 // With UTF-8, a character is encoded using 1, 2, 3 or 4 bytes, so (value.length() <= value.getBytes().length <= 4 * value.length()) 214 // As a result, value.getBytes().length <= limit 215 return value; 216 } 217 218 // There is a doubt, the string may need to be truncated (or not) 219 byte[] valueBytes = value.getBytes(StandardCharsets.UTF_8); 220 int bytesLength = valueBytes.length; 221 if (bytesLength <= __SOLR_STRING_NB_BYTES_LIMIT) 222 { 223 return value; 224 } 225 226 if (ObjectUtils.allNotNull(logger, documentId, fieldName)) 227 { 228 logger.warn("The string value for document '{}' and field name '{}' is longer ({}) than the max bytes length {}. It will be truncated to prevent Solr error, but you should consider verifying why this string is so long.", documentId, fieldName, bytesLength, __SOLR_STRING_NB_BYTES_LIMIT); 229 } 230 231 // Need a truncation (inspired by https://stackoverflow.com/questions/119328/how-do-i-truncate-a-java-string-to-fit-in-a-given-number-of-bytes-once-utf-8-en#answer-35148974) 232 CharBuffer charBuffer = CharBuffer.allocate(__SOLR_STRING_NB_BYTES_LIMIT); 233 CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder() 234 .onMalformedInput(CodingErrorAction.IGNORE); 235 decoder.decode(ByteBuffer.wrap(valueBytes, 0, __SOLR_STRING_NB_BYTES_LIMIT), charBuffer, true); 236 decoder.flush(charBuffer); 237 return new String(charBuffer.array(), 0, charBuffer.position()); 238 } 239 240 @Override 241 public void service(ServiceManager serviceManager) throws ServiceException 242 { 243 _manager = serviceManager; 244 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 245 _schemaDefProviderEP = (SchemaDefinitionProviderExtensionPoint) serviceManager.lookup(SchemaDefinitionProviderExtensionPoint.ROLE); 246 _schemaHelper = (SchemaHelper) serviceManager.lookup(SchemaHelper.ROLE); 247 _solrContentIndexer = (SolrContentIndexer) serviceManager.lookup(SolrContentIndexer.ROLE); 248 _solrWorkflowIndexer = (SolrWorkflowIndexer) serviceManager.lookup(SolrWorkflowIndexer.ROLE); 249 _solrResourceIndexer = (SolrResourceIndexer) serviceManager.lookup(SolrResourceIndexer.ROLE); 250 _solrTrashElementIndexer = (SolrTrashElementIndexer) serviceManager.lookup(SolrTrashElementIndexer.ROLE); 251 _solrClientProvider = (SolrClientProvider) serviceManager.lookup(SolrClientProvider.ROLE); 252 _workspaceSelector = (WorkspaceSelector) serviceManager.lookup(WorkspaceSelector.ROLE); 253 _readAccessHelper = (ReadAccessHelper) serviceManager.lookup(ReadAccessHelper.ROLE); 254 _repository = (JackrabbitRepository) serviceManager.lookup(AbstractRepository.ROLE); 255 _threadIndexerHelper = (ThreadIndexerHelper) serviceManager.lookup(ThreadIndexerHelper.ROLE); 256 } 257 258 @Override 259 public void initialize() throws Exception 260 { 261 Config config = Config.getInstance(); 262 _solrCorePrefix = config.getValue("cms.solr.core.prefix"); 263 264 _ametysInternalUrl = config.getValue("cms.solr.core.ametys.internal.url"); 265 if (StringUtils.isBlank(_ametysInternalUrl)) 266 { 267 // fallback to CMS URL as internal URL is an optional parameter 268 _ametysInternalUrl = config.getValue("cms.url"); 269 } 270 } 271 272 @Override 273 public void contextualize(Context context) throws ContextException 274 { 275 _context = context; 276 _cocoonContext = (org.apache.cocoon.environment.Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT); 277 } 278 279 /** 280 * Gets the 'autocommit' Solr client 281 * @param workspaceName The name of the workspace 282 * @return the Solr client 283 */ 284 protected SolrClient _getAutoCommitSolrClient(String workspaceName) 285 { 286 return _solrClientProvider.getUpdateClient(workspaceName, true); 287 } 288 289 /** 290 * Gets the 'no autocommit' Solr client 291 * @param workspaceName The name of the workspace 292 * @return the Solr client 293 */ 294 protected SolrClient _getNoAutoCommitSolrClient(String workspaceName) 295 { 296 return _solrClientProvider.getUpdateClient(workspaceName, false); 297 } 298 299 // for admin operations 300 private SolrClient _defaultSolrClient() 301 { 302 return _solrClientProvider.getUpdateClient(RepositoryConstants.DEFAULT_WORKSPACE); 303 } 304 305 /** 306 * Get the names of the Solr cores. 307 * @return The names of the Solr cores. 308 * @throws IOException If an I/O error occurs. 309 * @throws SolrServerException If a Solr error occurs. 310 */ 311 public Set<String> getCoreNames() throws IOException, SolrServerException 312 { 313 Set<String> coreNames = new HashSet<>(); 314 315 getLogger().debug("Getting core list."); 316 317 CoreAdminRequest req = new CoreAdminRequest(); 318 req.setAction(CoreAdminAction.STATUS); 319 320 NamedList<NamedList<Object>> status = req.process(_defaultSolrClient()).getCoreStatus(); 321 for (Map.Entry<String, NamedList<Object>> core : status) 322 { 323 String fullName = (String) core.getValue().get("name"); 324 if (fullName.startsWith(_solrCorePrefix)) 325 { 326 coreNames.add(fullName.substring(_solrCorePrefix.length())); 327 } 328 } 329 330 return coreNames; 331 } 332 333 /** 334 * Get the names of the Solr cores. 335 * @return The names of the Solr cores. 336 * @throws IOException If an I/O error occurs. 337 * @throws SolrServerException If a Solr error occurs. 338 */ 339 protected Set<String> getRealCoreNames() throws IOException, SolrServerException 340 { 341 Set<String> coreNames = new HashSet<>(); 342 343 getLogger().debug("Getting core list."); 344 345 CoreAdminResponse response = new CoreAdminRequest().process(_defaultSolrClient()); 346 347 NamedList<NamedList<Object>> status = response.getCoreStatus(); 348 for (Map.Entry<String, NamedList<Object>> core : status) 349 { 350 String fullName = (String) core.getValue().get("name"); 351 if (fullName.startsWith(_solrCorePrefix)) 352 { 353 coreNames.add(fullName.substring(_solrCorePrefix.length())); 354 } 355 } 356 357 return coreNames; 358 } 359 360 /** 361 * Create a Solr core. 362 * @param name The name of the core to create. 363 * @throws IOException If an I/O error occurs. 364 * @throws SolrServerException If a Solr error occurs. 365 */ 366 public void createCore(String name) throws IOException, SolrServerException 367 { 368 Set<String> cores = getCoreNames(); 369 if (!cores.contains(name)) 370 { 371 String fullName = _solrCorePrefix + name; 372 String configsetName = _CONFIGSET_NAME_PREFIX + (_solrCorePrefix.endsWith("-") ? _solrCorePrefix.substring(0, _solrCorePrefix.length() - 1) : _solrCorePrefix); 373 _createConfigset(configsetName); 374 375 getLogger().info("Creating core '{}' (full name: '{}').", name, fullName); 376 377 Create createRequest = new Create() { 378 @Override 379 public SolrParams getParams() 380 { 381 ModifiableSolrParams params = new ModifiableSolrParams(); 382 383 params.set(CoreAdminParams.ACTION, CoreAdminAction.CREATE.toString()); 384 params.set(CoreAdminParams.NAME, fullName); 385 params.set(CoreAdminParams.CONFIGSET, configsetName); 386 params.set("property.ametys.url", _ametysInternalUrl); 387 388 return params; 389 } 390 }; 391 392 NamedList<?> results = createRequest.process(_defaultSolrClient()).getResponse(); 393 394 NamedList<?> error = (NamedList<?>) results.get("error"); 395 if (error != null) 396 { 397 throw new IOException("Error creating the core: " + error.get("msg")); 398 } 399 } 400 else 401 { 402 if (getLogger().isDebugEnabled()) 403 { 404 getLogger().debug("Core '" + name + "' already exists, skipping it."); 405 } 406 } 407 } 408 409 /** 410 * Updates the ametys.url property of the Solr cores. 411 */ 412 public void updateAmetysUrlCoreProperty() 413 { 414 Set<String> coreNames; 415 try 416 { 417 coreNames = getCoreNames(); 418 } 419 catch (SolrServerException | IOException e) 420 { 421 getLogger().error("Cannot get Solr core names. As a result, the internal Ametys URL could not be updated on Solr server.", e); 422 return; 423 } 424 425 for (String coreName : coreNames) 426 { 427 String collection = _solrClientProvider.getCollectionName(coreName); 428 SolrResponseBase response; 429 try 430 { 431 response = new UpdateCorePropertyRequest("ametys.url", _ametysInternalUrl).process(_getAutoCommitSolrClient(coreName), collection); 432 } 433 catch (SolrServerException | IOException e) 434 { 435 getLogger().error("'core.properties' file updating for workspace '{}' did not succeed as expected.", coreName, e); 436 continue; 437 } 438 439 NamedList<Object> responseParams = response.getResponse(); 440 if ("ok".equals(responseParams.get("result"))) 441 { 442 Boolean valueChanged = responseParams.getBooleanArg("valueChanged"); 443 if (valueChanged) 444 { 445 getLogger().info("'core.properties' file updated with the up-to-date Ametys URL for workspace '{}'", coreName); 446 } 447 else 448 { 449 getLogger().info("'core.properties' file already has the up-to-date Ametys URL for workspace '{}', it was not modified.", coreName); 450 } 451 } 452 else 453 { 454 getLogger().error("'core.properties' file updating for workspace '{}' did not succeed as expected.", coreName); 455 } 456 } 457 } 458 459 private void _createConfigset(String name) throws IOException, SolrServerException 460 { 461 getLogger().info("Creating (if necessary) configset '{}'", name); 462 463 // This request handler will check if configset exists. If not it will be created. 464 CoreAdminRequest request = new CoreAdminRequest() { 465 @Override 466 public SolrParams getParams() 467 { 468 ModifiableSolrParams params = new ModifiableSolrParams(); 469 params.set(CoreAdminParams.ACTION, "createConfigset"); 470 params.set(CoreAdminParams.NAME, name); 471 return params; 472 } 473 }; 474 475 NamedList<?> results = request.process(_defaultSolrClient()).getResponse(); 476 477 NamedList<?> error = (NamedList<?>) results.get("error"); 478 if (error != null) 479 { 480 throw new IOException("Error creating the core: " + error.get("msg")); 481 } 482 } 483 484 /** 485 * Delete a Solr core. 486 * @param name The name of the core to delete. 487 * @throws IOException If an I/O error occurs. 488 * @throws SolrServerException If a Solr error occurs. 489 */ 490 public void deleteCore(String name) throws IOException, SolrServerException 491 { 492 String fullName = _solrCorePrefix + name; 493 494 getLogger().info("Deleting core '{}' (full name: '{}').", name, fullName); 495 496 CoreAdminResponse response = CoreAdminRequest.unloadCore(fullName, true, true, _defaultSolrClient()); 497 NamedList<?> results = response.getResponse(); 498 499 NamedList<?> error = (NamedList<?>) results.get("error"); 500 if (error != null) 501 { 502 throw new IOException("Error deleting core" + name + ": " + error.get("msg")); 503 } 504 } 505 506 /** 507 * Send the schema. 508 * @throws IOException If a communication error occurs. 509 * @throws SolrServerException If a solr error occurs. 510 */ 511 public void sendSchema() throws IOException, SolrServerException 512 { 513 getLogger().info("Computing and sending the schema to the solr server."); 514 515 String workspaceName = _workspaceSelector.getWorkspace(); 516 String collection = _solrClientProvider.getCollectionName(workspaceName); 517 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 518 519// SchemaRepresentation staticSchema = _schemaHelper.getStaticSchema(); 520 SchemaRepresentation staticSchema = _schemaHelper.getSchema("resource://org/ametys/cms/search/solr/schema/schema.xml"); 521 522 // TODO Clear the schema except fields marked ametysReadOnly="true". 523 524 // Clear the current schema. 525 clearSchema(solrClient, collection); 526 527 SchemaRequest schemaRequest = new SchemaRequest(); 528 SchemaResponse schemaResponse = schemaRequest.process(solrClient, collection); 529 530 // The cleared schema contains only the basic fields which can't be deleted. 531 SchemaRepresentation clearedSchema = schemaResponse.getSchemaRepresentation(); 532 SchemaFields schemaFields = new SchemaFields(clearedSchema); 533 534 getLogger().debug("Schema after clear: \n{}", schemaFields.toString()); 535 536 // Add the static schema types and fields. 537 List<SchemaRequest.Update> updates = new ArrayList<>(); 538 539 // Set "add field" definitions from the static schema to the update list. 540 addStaticSchemaUpdates(updates, staticSchema, schemaFields); 541 542 getLogger().debug("Temporary schema after static add: \n{}", schemaFields.toString()); 543 544 // Set "add field" definitions from the static schema to the update list. 545 addCustomUpdates(updates, schemaFields); 546 547 updates.sort(new SchemaRequestComparator()); 548 549 SchemaRequest.MultiUpdate multiUpdate = new SchemaRequest.MultiUpdate(updates); 550 SchemaResponse.UpdateResponse updateResponse = multiUpdate.process(solrClient, collection); 551 552 getLogger().debug("Send schema response: {}", updateResponse.toString()); 553 Object errors = updateResponse.getResponse().get("errors"); 554 if (errors != null && errors instanceof List && !((List) errors).isEmpty()) 555 { 556 String msg = "An error occured with the sent schema to Solr, it contains errors:\n" + errors.toString(); 557 throw new SolrServerException(msg); 558 } 559 560 getLogger().info("Schema sent to the solr server."); 561 562 reloadCores(); 563 } 564 565 /** 566 * Compute the list of {@link Update} directives from the static schema. 567 * @param updates The list of {@link Update} directives to fill. 568 * @param staticSchema The static schema representation. 569 * @param schemaFields The current schema fields, used to track the existing fields (to be filled). 570 */ 571 protected void addStaticSchemaUpdates(List<SchemaRequest.Update> updates, SchemaRepresentation staticSchema, SchemaFields schemaFields) 572 { 573 List<FieldTypeDefinition> fieldTypes = staticSchema.getFieldTypes(); 574 for (FieldTypeDefinition fieldType : fieldTypes) 575 { 576 String name = (String) fieldType.getAttributes().get("name"); 577 if (!schemaFields.hasFieldType(name)) 578 { 579 updates.add(new SchemaRequest.AddFieldType(fieldType)); 580 schemaFields.addFieldType(name); 581 } 582 } 583 for (Map<String, Object> field : staticSchema.getFields()) 584 { 585 String name = (String) field.get("name"); 586 if (!schemaFields.hasField(name)) 587 { 588 updates.add(new SchemaRequest.AddField(field)); 589 schemaFields.addField(name); 590 } 591 } 592 for (Map<String, Object> field : staticSchema.getDynamicFields()) 593 { 594 String name = (String) field.get("name"); 595 if (!schemaFields.hasDynamicField(name)) 596 { 597 updates.add(new SchemaRequest.AddDynamicField(field)); 598 schemaFields.addDynamicField(name); 599 } 600 } 601 for (Map<String, Object> field : staticSchema.getCopyFields()) 602 { 603 String source = (String) field.get("source"); 604 String dest = (String) field.get("dest"); 605 if (!schemaFields.hasCopyField(source, dest)) 606 { 607 updates.add(new SchemaRequest.AddCopyField(source, Arrays.asList(dest))); 608 schemaFields.addCopyField(source, dest); 609 } 610 } 611 } 612 613 /** 614 * Compute the list of custom {@link Update} directives. 615 * @param updates The list of {@link Update} directives to fill. 616 * @param schemaFields The current schema fields, used to track the existing fields (to be filled). 617 */ 618 protected void addCustomUpdates(List<SchemaRequest.Update> updates, SchemaFields schemaFields) 619 { 620 // Add all our property-managed fields. 621 for (String providerId : _schemaDefProviderEP.getExtensionsIds()) 622 { 623 SchemaDefinitionProvider definitionProvider = _schemaDefProviderEP.getExtension(providerId); 624 625 for (SchemaDefinition definition : definitionProvider.getDefinitions()) 626 { 627 if (!definition.exists(schemaFields)) 628 { 629 SchemaRequest.Update update = definition.getSchemaUpdate(); 630 if (update != null) 631 { 632 updates.add(update); 633 } 634 } 635 } 636 } 637 } 638 639 /** 640 * Delete all the fields of the existing schema in the given collection. 641 * @param solrClient The Solr client 642 * @param collection The collection. 643 * @throws IOException If a communication error occurs. 644 * @throws SolrServerException If a solr error occurs. 645 */ 646 protected void clearSchema(SolrClient solrClient, String collection) throws IOException, SolrServerException 647 { 648 try 649 { 650 getLogger().info("Clearing the existing schema on the solr server."); 651 652 SchemaRequest schemaRequest = new SchemaRequest(); 653 SchemaResponse schemaResponse = schemaRequest.process(solrClient, collection); 654 655 SchemaRepresentation schema = schemaResponse.getSchemaRepresentation(); 656 657 List<SchemaRequest.Update> deletions = new ArrayList<>(); 658 659 // First the copy fields, then dynamic and simple fields, and field types in the end. 660 for (Map<String, Object> field : schema.getCopyFields()) 661 { 662 String source = (String) field.get("source"); 663 String dest = (String) field.get("dest"); 664 deletions.add(new SchemaRequest.DeleteCopyField(source, Arrays.asList(dest))); 665 } 666 for (Map<String, Object> field : schema.getDynamicFields()) 667 { 668 String name = (String) field.get("name"); 669 deletions.add(new SchemaRequest.DeleteDynamicField(name)); 670 } 671 for (Map<String, Object> field : schema.getFields()) 672 { 673 String name = (String) field.get("name"); 674 if (!_READ_ONLY_FIELDS.contains(name)) 675 { 676 deletions.add(new SchemaRequest.DeleteField(name)); 677 } 678 } 679 for (FieldTypeDefinition fieldType : schema.getFieldTypes()) 680 { 681 String name = (String) fieldType.getAttributes().get("name"); 682 if (!_READ_ONLY_FIELDTYPES.contains(name)) 683 { 684 deletions.add(new SchemaRequest.DeleteFieldType(name)); 685 } 686 } 687 688 SchemaRequest.MultiUpdate multiUpdate = new SchemaRequest.MultiUpdate(deletions); 689 SchemaResponse.UpdateResponse updateResponse = multiUpdate.process(solrClient, collection); 690 691 Object errors = updateResponse.getResponse().get("errors"); 692 if (errors != null && errors instanceof List && !((List) errors).isEmpty()) 693 { 694 String msg = "An error occured when clearing Solr schema, it contains errors:\n" + errors.toString(); 695 throw new SolrServerException(msg); 696 } 697 else 698 { 699 getLogger().debug("Clear schema response: {}", updateResponse.toString()); 700 } 701 702 getLogger().info("Solr schema cleared."); 703 } 704 catch (SolrServerException | IOException e) 705 { 706 getLogger().error("Error clearing schema in collection " + collection, e); 707 throw e; 708 } 709 } 710 711 /** 712 * Reload the solr cores. 713 * @throws IOException If a communication error occurs. 714 * @throws SolrServerException If a solr error occurs. 715 */ 716 protected void reloadCores() throws IOException, SolrServerException 717 { 718 getLogger().info("Reloading solr cores."); 719 720 for (String coreName : getCoreNames()) 721 { 722 String fullName = _solrCorePrefix + coreName; 723 724 CoreAdminResponse reloadResponse = CoreAdminRequest.reloadCore(fullName, _defaultSolrClient()); 725 726 getLogger().debug("Reload core response: {}", reloadResponse.toString()); 727 } 728 729 getLogger().info("All cores reloaded."); 730 } 731 732 /** 733 * Reloads the ACL Solr cache for all users 734 * @throws IOException If an I/O error occurs. 735 * @throws SolrServerException If a Solr error occurs. 736 * @throws RepositoryException If a repository exception occurs when retrieving workspaces. 737 */ 738 public void reloadAclCache() throws IOException, SolrServerException, RepositoryException 739 { 740 String[] workspaceNames = _repository.getWorkspaces(); 741 for (String workspaceName : workspaceNames) 742 { 743 reloadAclCache(workspaceName); 744 } 745 } 746 747 /** 748 * Reloads the ACL Solr cache for all users 749 * @param workspaceName The workspace name 750 * @throws IOException If an I/O error occurs. 751 * @throws SolrServerException If a Solr error occurs. 752 */ 753 public void reloadAclCache(String workspaceName) throws IOException, SolrServerException 754 { 755 reloadAclCache(workspaceName, false); 756 } 757 758 /** 759 * Reloads the ACL Solr cache for all users 760 * @param workspaceName The workspace name 761 * @param checkIfNecessary true to check if the reload is necessary for each segment (i.e. reload only the segments not already in cache) 762 * @throws IOException If an I/O error occurs. 763 * @throws SolrServerException If a Solr error occurs. 764 */ 765 public void reloadAclCache(String workspaceName, boolean checkIfNecessary) throws IOException, SolrServerException 766 { 767 getLogger().info("Reloading read ACL Solr cache for workspace '{}' (checkIfNecessary={})", workspaceName, checkIfNecessary); 768 769 String collection = _solrClientProvider.getCollectionName(workspaceName); 770 SolrResponseBase responseBase = new ReloadAclCacheRequest(checkIfNecessary).process(_getAutoCommitSolrClient(workspaceName), collection); 771 NamedList<Object> responseObj = responseBase.getResponse(); 772 773 if ("ok".equals(responseObj.get("result"))) 774 { 775 getLogger().info("Read-ACL Solr cache reloaded for workspace '{}' (checkIfNecessary={})", workspaceName, checkIfNecessary); 776 } 777 else 778 { 779 Object error = responseObj.get("error"); 780 getLogger().error("The reloading of Read-ACL Solr Cache for workspace '{}' (checkIfNecessary={}) did not succeed as expected.\n Error code is the following: {}", workspaceName, checkIfNecessary, error); 781 } 782 } 783 784 /** 785 * Updates the ACL Solr cache for some {@link AmetysObject}s for all workspaces. 786 * @param objects the {@link AmetysObject}s to update. 787 * @throws IOException If an I/O error occurs. 788 * @throws SolrServerException If a Solr error occurs. 789 * @throws RepositoryException If a repository exception occurs when retrieving workspaces. 790 */ 791 public void updateAclCache(Iterable<? extends AmetysObject> objects) throws IOException, SolrServerException, RepositoryException 792 { 793 String[] workspaceNames = _repository.getWorkspaces(); 794 for (String workspaceName : workspaceNames) 795 { 796 updateAclCache(objects, workspaceName); 797 } 798 } 799 800 /** 801 * Updates the ACL Solr cache for some {@link AmetysObject}s. 802 * @param objects the {@link AmetysObject}s to update. 803 * @param workspaceName The workspace name 804 * @throws IOException If an I/O error occurs. 805 * @throws SolrServerException If a Solr error occurs. 806 * @throws RepositoryException If a repository exception occurs when retrieving workspaces. 807 */ 808 public void updateAclCache(Iterable<? extends AmetysObject> objects, String workspaceName) throws IOException, SolrServerException, RepositoryException 809 { 810 Map<String, Map<String, Object>> solrParams = new HashMap<>(); 811 812 for (AmetysObject object : objects) 813 { 814 AllowedUsers allowedUsers = _readAccessHelper.allowedUsers(object); 815 816 solrParams.put(object.getId(), Map.of("anonymous", allowedUsers.isAnonymousAllowed(), 817 "anyConnectedUser", allowedUsers.isAnyConnectedUserAllowed(), 818 "allowedUsers", allowedUsers.getAllowedUsers().stream().map(UserIdentity::userIdentityToString).collect(Collectors.toList()), 819 "deniedUsers", allowedUsers.getDeniedUsers().stream().map(UserIdentity::userIdentityToString).collect(Collectors.toList()), 820 "allowedGroups", allowedUsers.getAllowedGroups().stream().map(GroupIdentity::groupIdentityToString).collect(Collectors.toList()), 821 "deniedGroups", allowedUsers.getDeniedGroups().stream().map(GroupIdentity::groupIdentityToString).collect(Collectors.toList()))); 822 } 823 824 String collection = _solrClientProvider.getCollectionName(workspaceName); 825 SolrResponse response = new UpdateAclCacheRequest(solrParams).process(_getAutoCommitSolrClient(workspaceName), collection); 826 NamedList<Object> responseObj = response.getResponse(); 827 828 if ("ok".equals(responseObj.get("result"))) 829 { 830 if (getLogger().isInfoEnabled()) 831 { 832 getLogger().info("Read-ACL Solr cache updated for workspace '{}' and objects {}", workspaceName, solrParams.keySet()); 833 } 834 } 835 else 836 { 837 @SuppressWarnings("unchecked") 838 List<String> unHandledObjects = (List<String>) responseObj.get("unhandled-objects"); 839 getLogger().warn("The updating of Read-ACL Solr Cache for workspace '{}' did not succeed as expected.\n Following objects have net been updated: {}", workspaceName, unHandledObjects); 840 } 841 } 842 843 /** 844 * Index all the contents in a given workspace. 845 * @param workspaceName the workspace where to index 846 * @param indexAttachments to index content attachments 847 * @param solrClient The solr client to use 848 * @return The indexation result as a Map. 849 * @throws Exception if an error occurs while indexing. 850 */ 851 public Map<String, Object> indexAllContents(String workspaceName, boolean indexAttachments, SolrClient solrClient) throws Exception 852 { 853 return indexAllContents(workspaceName, indexAttachments, solrClient, ProgressionTrackerFactory.createSimpleProgressionTracker("Index all contents", getLogger())); 854 } 855 856 /** 857 * Index all the contents in a given workspace. 858 * @param workspaceName the workspace where to index 859 * @param indexAttachments to index content attachments 860 * @param solrClient The solr client to use 861 * @param progressionTracker The progression of the indexation 862 * @return The indexation result as a Map. 863 * @throws Exception if an error occurs while indexing. 864 */ 865 public Map<String, Object> indexAllContents(String workspaceName, boolean indexAttachments, SolrClient solrClient, SimpleProgressionTracker progressionTracker) throws Exception 866 { 867 Request request = ContextHelper.getRequest(_context); 868 869 // Retrieve the current workspace. 870 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 871 872 try 873 { 874 // Force the workspace. 875 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 876 877 getLogger().info("Starting the indexation of all contents for workspace {}", workspaceName); 878 879 long start = System.currentTimeMillis(); 880 881 // Delete all contents 882 unindexAllContents(workspaceName, indexAttachments, solrClient); 883 884 String query = ContentQueryHelper.getContentXPathQuery(null); 885 AmetysObjectIterable<Content> contents = _resolver.query(query); 886 887 IndexationResult result = doIndexContents(contents, workspaceName, indexAttachments, solrClient, progressionTracker); 888 889 long end = System.currentTimeMillis(); 890 891 if (!result.hasErrors()) 892 { 893 getLogger().info("{} contents indexed without error in {} milliseconds.", result.successCount(), end - start); 894 } 895 else 896 { 897 getLogger().info("Content indexation ended, the process took {} milliseconds. {} contents were not indexed successfully, please review the error logs above for more details.", end - start, result.errorCount()); 898 } 899 900 Map<String, Object> results = new HashMap<>(); 901 results.put("successCount", result.successCount()); 902 if (result.hasErrors()) 903 { 904 results.put("errorCount", result.errorCount()); 905 } 906 907 return results; 908 } 909 catch (Exception e) 910 { 911 String error = String.format("Failed to index all contents in workspace %s", workspaceName); 912 getLogger().error(error, e); 913 throw new IndexingException(error, e); 914 } 915 finally 916 { 917 // Restore context 918 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 919 } 920 } 921 922 /** 923 * Unindex all content documents. 924 * @param workspaceName The workspace name 925 * @param unindexAttachments also unindex content attachments 926 * @param solrClient The solr client to use 927 * @throws Exception if an error occurs while unindexing. 928 */ 929 protected void unindexAllContents(String workspaceName, boolean unindexAttachments, SolrClient solrClient) throws Exception 930 { 931 String collection = _solrClientProvider.getCollectionName(workspaceName); 932 933 Query contents = new DocumentTypeQuery(SolrFieldNames.TYPE_CONTENT); 934 Query query; 935 if (unindexAttachments) 936 { 937 Query contentResourceAttachments = new DocumentTypeQuery(SolrFieldNames.TYPE_CONTENT_ATTACHMENT_RESOURCE); 938 Query contentResourceAttributes = new DocumentTypeQuery(SolrFieldNames.TYPE_CONTENT_ATTRIBUTE_RESOURCE); 939 query = new OrQuery(contentResourceAttachments, contentResourceAttributes, contents); 940 } 941 else 942 { 943 query = contents; 944 } 945 solrClient.deleteByQuery(collection, query.build()); 946 } 947 948 /** 949 * Add or update the child contents of a {@link AmetysObjectCollection} into Solr index, for all workspaces and commit 950 * @param collectionId The id of collection 951 * @param indexAttachments to index content attachments 952 * @throws Exception if an error occurs while indexing. 953 */ 954 public void indexSubcontents(String collectionId, boolean indexAttachments) throws Exception 955 { 956 String[] workspaceNames = _repository.getWorkspaces(); 957 for (String workspaceName : workspaceNames) 958 { 959 indexSubcontents(collectionId, workspaceName, indexAttachments); 960 } 961 } 962 963 /** 964 * Index the child contents of a {@link AmetysObjectCollection} 965 * @param collectionId The id of collection 966 * @param workspaceName the workspace where to index 967 * @param indexAttachments to index content attachments 968 * @throws Exception if an error occurs while unindexing. 969 */ 970 public void indexSubcontents(String collectionId, String workspaceName, boolean indexAttachments) throws Exception 971 { 972 Request request = ContextHelper.getRequest(_context); 973 974 // Retrieve the current workspace. 975 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 976 977 try 978 { 979 // Force the workspace. 980 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 981 982 if (_resolver.hasAmetysObjectForId(collectionId)) 983 { 984 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 985 AmetysObjectCollection collection = _resolver.resolveById(collectionId); 986 AmetysObjectIterable<AmetysObject> children = collection.getChildren(); 987 988 for (AmetysObject child : children) 989 { 990 if (child instanceof Content) 991 { 992 Content content = (Content) child; 993 994 _doIndexContent(content, workspaceName, indexAttachments, solrClient); 995 } 996 } 997 } 998 } 999 finally 1000 { 1001 // Restore context 1002 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1003 } 1004 } 1005 1006 /** 1007 * Add or update a content into Solr index on all workspaces and commit 1008 * @param contentId The id of the content to index 1009 * @param indexAttachments to index content attachments 1010 * @throws Exception if an error occurs while indexing. 1011 */ 1012 public void indexContent(String contentId, boolean indexAttachments) throws Exception 1013 { 1014 String[] workspaceNames = _repository.getWorkspaces(); 1015 for (String workspaceName : workspaceNames) 1016 { 1017 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1018 indexContent(contentId, workspaceName, indexAttachments, solrClient); 1019 } 1020 } 1021 1022 /** 1023 * Add or update a content into Solr index 1024 * @param contentId The id of the content to index 1025 * @param workspaceName the workspace where to index 1026 * @param indexAttachments to index content attachments 1027 * @throws Exception if an error occurs while indexing. 1028 */ 1029 public void indexContent(String contentId, String workspaceName, boolean indexAttachments) throws Exception 1030 { 1031 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1032 indexContent(contentId, workspaceName, indexAttachments, solrClient); 1033 } 1034 1035 /** 1036 * Add or update a content into Solr index 1037 * @param contentId The id of the content to index 1038 * @param workspaceName the workspace where to index 1039 * @param indexAttachments to index content attachments 1040 * @param solrClient The solr client to use 1041 * @throws Exception if an error occurs while indexing. 1042 */ 1043 public void indexContent(String contentId, String workspaceName, boolean indexAttachments, SolrClient solrClient) throws Exception 1044 { 1045 Request request = ContextHelper.getRequest(_context); 1046 1047 // Retrieve the current workspace. 1048 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1049 1050 try 1051 { 1052 // Force the workspace. 1053 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1054 1055 if (_resolver.hasAmetysObjectForId(contentId)) 1056 { 1057 Content content = _resolver.resolveById(contentId); 1058 _doIndexContent(content, workspaceName, indexAttachments, solrClient); 1059 } 1060 } 1061 finally 1062 { 1063 // Restore context 1064 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1065 } 1066 } 1067 1068 private void _doIndexContent(Content content, String workspaceName, boolean indexAttachments, SolrClient solrClient) throws IndexingException 1069 { 1070 try 1071 { 1072 long time_0 = System.currentTimeMillis(); 1073 1074 getLogger().debug("Indexing content {} into Solr for workspace {}", content.getId(), workspaceName); 1075 1076 deleteRepeaterDocs(content.getId(), workspaceName, solrClient); 1077 doIndexContent(content, workspaceName, solrClient); 1078 doIndexContentWorkflow(content, workspaceName, solrClient); 1079 if (indexAttachments) 1080 { 1081 indexContentAttachments(content.getRootAttachments(), content, solrClient); 1082 } 1083 1084 getLogger().debug("Successfully indexed content {} in Solr in {} ms", content.getId(), System.currentTimeMillis() - time_0); 1085 } 1086 catch (Exception e) 1087 { 1088 String error = String.format("Failed to index content %s in workspace %s", content.getId(), workspaceName); 1089 getLogger().error(error, e); 1090 throw new IndexingException(error, e); 1091 } 1092 } 1093 1094 /** 1095 * Send a collection of contents for indexation in the solr server on all workspaces and commit 1096 * @param contents the collection of contents to index. 1097 * @return the indexation result. 1098 * @throws Exception if an error occurs while indexing. 1099 */ 1100 public IndexationResult indexContents(Iterable<Content> contents) throws Exception 1101 { 1102 return indexContents(contents, ProgressionTrackerFactory.createContainerProgressionTracker("Index contents", getLogger())); 1103 } 1104 1105 1106 /** 1107 * Send a collection of contents for indexation in the solr server on all workspaces and commit 1108 * @param contents the collection of contents to index. 1109 * @param progressionTracker The progression of the indexation 1110 * @return the indexation result. 1111 * @throws Exception if an error occurs while indexing. 1112 */ 1113 public IndexationResult indexContents(Iterable<Content> contents, ContainerProgressionTracker progressionTracker) throws Exception 1114 { 1115 IndexationResult result = new IndexationResult(0, 0); 1116 1117 String[] workspaceNames = _repository.getWorkspaces(); 1118 1119 for (String workspaceName : workspaceNames) 1120 { 1121 progressionTracker.addSimpleStep(workspaceName, new I18nizableText("plugin.cms", "PLUGINS_CMS_SCHEDULER_GLOBAL_INDEXATION_CONTENT_WORKSPACE_STEP_LABEL", List.of(workspaceName))); 1122 } 1123 1124 for (String workspaceName : workspaceNames) 1125 { 1126 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1127 IndexationResult wResult = indexContents(contents, workspaceName, true, solrClient, progressionTracker.getStep(workspaceName)); 1128 1129 result = new IndexationResult(result.successCount() + wResult.successCount(), result.errorCount() + wResult.errorCount()); 1130 } 1131 1132 return result; 1133 } 1134 1135 /** 1136 * Send a collection of contents for indexation in the solr server. 1137 * @param contents the collection of contents to index. 1138 * @param workspaceName the workspace where to index 1139 * @param indexAttachments to index content attachments 1140 * @param solrClient The solr client to use 1141 * @return the indexation result. 1142 * @throws Exception if an error occurs while indexing. 1143 */ 1144 public IndexationResult indexContents(Iterable<Content> contents, String workspaceName, boolean indexAttachments, SolrClient solrClient) throws Exception 1145 { 1146 return indexContents(contents, workspaceName, indexAttachments, solrClient, ProgressionTrackerFactory.createSimpleProgressionTracker("Index contents for workspace " + workspaceName, getLogger())); 1147 } 1148 1149 /** 1150 * Send a collection of contents for indexation in the solr server. 1151 * @param contents the collection of contents to index. 1152 * @param workspaceName the workspace where to index 1153 * @param indexAttachments to index content attachments 1154 * @param solrClient The solr client to use 1155 * @param progressionTracker The progression of the indexation 1156 * @return the indexation result. 1157 * @throws Exception if an error occurs while indexing. 1158 */ 1159 public IndexationResult indexContents(Iterable<Content> contents, String workspaceName, boolean indexAttachments, SolrClient solrClient, SimpleProgressionTracker progressionTracker) throws Exception 1160 { 1161 Request request = ContextHelper.getRequest(_context); 1162 1163 // Retrieve the current workspace. 1164 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1165 1166 try 1167 { 1168 // Force the workspace. 1169 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1170 1171 getLogger().info("Starting indexation of several contents for workspace {}", workspaceName); 1172 1173 long start = System.currentTimeMillis(); 1174 1175 List<Content> contentsInWorkspace = StreamSupport.stream(contents.spliterator(), false) 1176 .map(Content::getId) 1177 .map(this::_resolveSilently) 1178 .filter(Objects::nonNull) 1179 .collect(Collectors.toList()); 1180 1181 IndexationResult result = doIndexContents(contentsInWorkspace, workspaceName, indexAttachments, solrClient, progressionTracker); 1182 1183 long end = System.currentTimeMillis(); 1184 1185 if (!result.hasErrors()) 1186 { 1187 getLogger().info("{} contents indexed without error in {} milliseconds.", result.successCount(), end - start); 1188 } 1189 else 1190 { 1191 getLogger().info("Content indexation ended, the process took {} milliseconds. {} contents were not indexed successfully, please review the error logs above for more details.", end - start, result.errorCount()); 1192 } 1193 1194 return result; 1195 } 1196 catch (Exception e) 1197 { 1198 String error = String.format("Failed to index several contents in workspace %s", workspaceName); 1199 getLogger().error(error, e); 1200 throw new IndexingException(error, e); 1201 } 1202 finally 1203 { 1204 // Restore context 1205 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1206 } 1207 } 1208 1209 private Content _resolveSilently(String contentId) 1210 { 1211 try 1212 { 1213 return _resolver.resolveById(contentId); 1214 } 1215 catch (UnknownAmetysObjectException e) 1216 { 1217 return null; 1218 } 1219 } 1220 1221 /** 1222 * Send some contents for indexation in the solr server. 1223 * @param contents the contents to index. 1224 * @param workspaceName The workspace name 1225 * @param indexAttachments to index content attachments 1226 * @param solrClient The solr client to use 1227 * @param progressionTracker The progression of the indexation 1228 * @return the indexation result. 1229 * @throws Exception if an error occurs committing the results. 1230 */ 1231 protected IndexationResult doIndexContents(Iterable<Content> contents, String workspaceName, boolean indexAttachments, SolrClient solrClient, SimpleProgressionTracker progressionTracker) throws Exception 1232 { 1233 int numberOfContents = IterableUtils.size(contents); 1234 progressionTracker.setSize(numberOfContents); 1235 1236 // Add callable for each content to index 1237 List<Future<Void>> tasks = new ArrayList<>(); 1238 for (Content content : contents) 1239 { 1240 tasks.add(_threadIndexerHelper.submitCallable(new ContentIndexerCallable(content, workspaceName, indexAttachments, solrClient, progressionTracker))); 1241 } 1242 1243 // Now that everything is submitted, we can iterate and wait for result 1244 return IndexationResult.fromTasks(tasks, getLogger()); 1245 } 1246 1247 /** 1248 * Update the value of a specific system property in a content document. 1249 * @param content The content to update. 1250 * @param propertyId The system property ID. 1251 * @param workspaceName The workspace name 1252 * @throws Exception if an error occurs while indexing. 1253 */ 1254 public void updateSystemProperty(Content content, String propertyId, String workspaceName) throws Exception 1255 { 1256 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1257 updateSystemProperty(content, propertyId, workspaceName, solrClient); 1258 } 1259 1260 /** 1261 * Update the value of a specific system property in a content document. 1262 * @param content The content to update. 1263 * @param propertyId The system property ID. 1264 * @param workspaceName The workspace name 1265 * @param solrClient The solr client to use 1266 * @throws Exception if an error occurs while indexing. 1267 */ 1268 public void updateSystemProperty(Content content, String propertyId, String workspaceName, SolrClient solrClient) throws Exception 1269 { 1270 getLogger().debug("Updating the system property '{}' for content {} into Solr.", propertyId, content); 1271 1272 SolrInputDocument document = new SolrInputDocument(); 1273 boolean hasUpdate = _solrContentIndexer.indexPartialSystemProperty(content, propertyId, document); 1274 1275 if (!hasUpdate) 1276 { 1277 getLogger().debug("Did not index '{}' system property for content {} in Solr because no update to apply.", propertyId, content); 1278 return; 1279 } 1280 1281 int status = _pushSolrDocument(document, workspaceName, solrClient); 1282 if (status != 0) 1283 { 1284 throw new IOException("Indexing of system property '" + propertyId + "': got status code '" + status + "'."); 1285 } 1286 1287 getLogger().debug("Succesfully indexed '{}' system property for content {} in Solr.", propertyId, content); 1288 } 1289 1290 /** 1291 * Update the value of a specific property in a content document. 1292 * @param content The content to update. 1293 * @param propertyId The system property ID. 1294 * @param workspaceName The workspace name 1295 * @throws Exception if an error occurs while indexing. 1296 */ 1297 public void updateProperty(Content content, String propertyId, String workspaceName) throws Exception 1298 { 1299 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1300 updateProperty(content, propertyId, workspaceName, solrClient); 1301 } 1302 1303 /** 1304 * Update the value of a specific property in a content document. 1305 * @param content The content to update. 1306 * @param propertyId The system property ID. 1307 * @param workspaceName The workspace name 1308 * @param solrClient The solr client to use 1309 * @throws Exception if an error occurs while indexing. 1310 */ 1311 public void updateProperty(Content content, String propertyId, String workspaceName, SolrClient solrClient) throws Exception 1312 { 1313 getLogger().debug("Updating the property '{}' for content {} into Solr.", propertyId, content); 1314 1315 SolrInputDocument document = new SolrInputDocument(); 1316 boolean hasUpdate = _solrContentIndexer.indexPartialProperty(content, propertyId, document); 1317 1318 if (!hasUpdate) 1319 { 1320 getLogger().debug("Did not index '{}' property for content {} in Solr because no update to apply.", propertyId, content); 1321 return; 1322 } 1323 1324 int status = _pushSolrDocument(document, workspaceName, solrClient); 1325 if (status != 0) 1326 { 1327 throw new IOException("Indexing of property '" + propertyId + "': got status code '" + status + "'."); 1328 } 1329 1330 getLogger().debug("Succesfully indexed '{}' property for content {} in Solr.", propertyId, content); 1331 } 1332 1333 private int _pushSolrDocument(SolrInputDocument document, String workspaceName, SolrClient solrClient) throws Exception 1334 { 1335 String collection = _solrClientProvider.getCollectionName(workspaceName); 1336 UpdateResponse solrResponse = solrClient.add(collection, document); 1337 return solrResponse.getStatus(); 1338 } 1339 1340 /** 1341 * Remove a content from Solr index for all workspaces and commit 1342 * @param contentId The id of content to unindex 1343 * @param unindexAttachments also unindex content attachments 1344 * @throws Exception if an error occurs while indexing. 1345 */ 1346 public void unindexContent(String contentId, boolean unindexAttachments) throws Exception 1347 { 1348 String[] workspaceNames = _repository.getWorkspaces(); 1349 for (String workspaceName : workspaceNames) 1350 { 1351 unindexContent(contentId, workspaceName, unindexAttachments); 1352 } 1353 } 1354 1355 /** 1356 * Remove a content from Solr index 1357 * @param contentId The id of content to unindex 1358 * @param workspaceName The workspace where to work in 1359 * @param unindexAttachments also unindex content attachments 1360 * @throws Exception if an error occurs while indexing. 1361 */ 1362 public void unindexContent(String contentId, String workspaceName, boolean unindexAttachments) throws Exception 1363 { 1364 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1365 unindexContent(contentId, workspaceName, unindexAttachments, solrClient); 1366 } 1367 1368 /** 1369 * Remove a content from Solr index 1370 * @param contentId The id of content to unindex 1371 * @param workspaceName The workspace where to work in 1372 * @param unindexAttachments also unindex content attachments 1373 * @param solrClient The solr client to use 1374 * @throws Exception if an error occurs while indexing. 1375 */ 1376 public void unindexContent(String contentId, String workspaceName, boolean unindexAttachments, SolrClient solrClient) throws Exception 1377 { 1378 Request request = ContextHelper.getRequest(_context); 1379 1380 // Retrieve the current workspace. 1381 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1382 1383 try 1384 { 1385 // Force the workspace. 1386 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1387 1388 getLogger().debug("Unindexing content {} from Solr for workspace {}", contentId, workspaceName); 1389 1390 deleteRepeaterDocs(contentId, workspaceName, solrClient); 1391 doUnindexDocument(contentId, workspaceName, solrClient); 1392 if (unindexAttachments) 1393 { 1394 doUnindexContentAttachments(contentId, workspaceName, solrClient); 1395 } 1396 _solrWorkflowIndexer.unindexAmetysObjectWorkflow(contentId, workspaceName, solrClient); 1397 1398 getLogger().debug("Succesfully deleted content {} from Solr.", contentId); 1399 } 1400 catch (Exception e) 1401 { 1402 String error = String.format("Failed to unindex content %s in workspace %s", contentId, workspaceName); 1403 getLogger().error(error, e); 1404 throw new IndexingException(error, e); 1405 } 1406 finally 1407 { 1408 // Restore workspace 1409 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1410 } 1411 } 1412 1413 /** 1414 * Remove a content from Solr index for all workspaces and commit 1415 * @param contentIds The id of content to unindex 1416 * @throws Exception if an error occurs while indexing. 1417 */ 1418 public void unindexContents(Collection<String> contentIds) throws Exception 1419 { 1420 String[] workspaceNames = _repository.getWorkspaces(); 1421 for (String workspaceName : workspaceNames) 1422 { 1423 unindexContents(contentIds, workspaceName); 1424 } 1425 } 1426 1427 /** 1428 * Remove a content from Solr index 1429 * @param contentIds The id of content to unindex 1430 * @param workspaceName The workspace where to work in 1431 * @throws Exception if an error occurs while indexing. 1432 */ 1433 public void unindexContents(Collection<String> contentIds, String workspaceName) throws Exception 1434 { 1435 Request request = ContextHelper.getRequest(_context); 1436 1437 // Retrieve the current workspace. 1438 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1439 1440 try 1441 { 1442 // Force the workspace. 1443 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1444 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1445 1446 getLogger().debug("Unindexing several contents from Solr."); 1447 1448 for (String contentId : contentIds) 1449 { 1450 deleteRepeaterDocs(contentId, workspaceName, solrClient); 1451 doUnindexDocument(contentId, workspaceName, solrClient); 1452 _solrWorkflowIndexer.unindexAmetysObjectWorkflow(contentId, workspaceName, solrClient); 1453 } 1454 1455 getLogger().debug("Succesfully unindexed content from Solr."); 1456 } 1457 catch (Exception e) 1458 { 1459 String error = String.format("Failed to unindex several contents in workspace %s", workspaceName); 1460 getLogger().error(error, e); 1461 throw new IndexingException(error, e); 1462 } 1463 finally 1464 { 1465 // Restore workspace 1466 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1467 } 1468 } 1469 1470 /** 1471 * Add or update a content into Solr index 1472 * @param content The content to index 1473 * @param workspaceName The workspace where to index 1474 * @param solrClient The solr client to use 1475 * @throws Exception if an error occurs while indexing. 1476 */ 1477 protected void doIndexContent(Content content, String workspaceName, SolrClient solrClient) throws Exception 1478 { 1479 long time_0 = System.currentTimeMillis(); 1480 1481 SolrInputDocument document = new SolrInputDocument(); 1482 List<SolrInputDocument> additionalDocuments = _solrContentIndexer.indexContent(content, document); 1483 1484 long time_1 = System.currentTimeMillis(); 1485 getLogger().debug("Populate indexing fields for content {} for workspace {} in {} ms", content.getId(), workspaceName, time_1 - time_0); 1486 1487 indexAclInitValues(content, document); 1488 1489 long time_2 = System.currentTimeMillis(); 1490 getLogger().debug("Populate ACL for content {} for workspace {} in {} ms", content.getId(), workspaceName, time_2 - time_1); 1491 1492 List<SolrInputDocument> documents = new ArrayList<>(additionalDocuments); 1493 documents.add(document); 1494 1495 UpdateResponse solrResponse = solrClient.add(_solrClientProvider.getCollectionName(workspaceName), documents); 1496 int status = solrResponse.getStatus(); 1497 1498 long time_3 = System.currentTimeMillis(); 1499 getLogger().debug("Update document for content {} for workspace {} in {} ms", content.getId(), workspaceName, time_3 - time_2); 1500 1501 if (status != 0) 1502 { 1503 throw new IOException("Content indexation: got status code '" + status + "'."); 1504 } 1505 } 1506 1507 /** 1508 * Indexes read-ACl initial values for object 1509 * @param ametysObject The object 1510 * @param document The Solr document 1511 */ 1512 public void indexAclInitValues(AmetysObject ametysObject, SolrInputDocument document) 1513 { 1514 // Indexation of AmetysObject property 1515 document.addField(SolrFieldNames.IS_AMETYS_OBJECT, true); 1516 1517 // Indexation of initValues for AllowedUsers 1518 AllowedUsers allowedUsers = _readAccessHelper.allowedUsers(ametysObject); 1519 document.addField(SolrFieldNames.ACL_INIT_VALUE_ANONYMOUS, allowedUsers.isAnonymousAllowed()); 1520 document.addField(SolrFieldNames.ACL_INIT_VALUE_ANYCONNECTED, allowedUsers.isAnyConnectedUserAllowed()); 1521 _addField(document, SolrFieldNames.ACL_INIT_VALUE_ALLOWED_USERS, allowedUsers.getAllowedUsers(), UserIdentity::userIdentityToString); 1522 _addField(document, SolrFieldNames.ACL_INIT_VALUE_DENIED_USERS, allowedUsers.getDeniedUsers(), UserIdentity::userIdentityToString); 1523 _addField(document, SolrFieldNames.ACL_INIT_VALUE_ALLOWED_GROUPS, allowedUsers.getAllowedGroups(), GroupIdentity::groupIdentityToString); 1524 _addField(document, SolrFieldNames.ACL_INIT_VALUE_DENIED_GROUPS, allowedUsers.getDeniedGroups(), GroupIdentity::groupIdentityToString); 1525 } 1526 1527 private <T> void _addField(SolrInputDocument document, String fieldName, Set<T> values, Function<T, String> stringifier) 1528 { 1529 if (values == null) 1530 { 1531 return; 1532 } 1533 1534 for (T value : values) 1535 { 1536 document.addField(fieldName, stringifier.apply(value)); 1537 } 1538 } 1539 1540 /** 1541 * Index the whole workflow of a content. 1542 * @param content The content. 1543 * @param workspaceName The workspace name 1544 * @param solrClient The solr client to use 1545 * @throws Exception if an error occurs while indexing. 1546 */ 1547 protected void doIndexContentWorkflow(Content content, String workspaceName, SolrClient solrClient) throws Exception 1548 { 1549 if (content instanceof WorkflowAwareContent) 1550 { 1551 _solrWorkflowIndexer.indexAmetysObjectWorkflow((WorkflowAwareContent) content, workspaceName, solrClient); 1552 } 1553 } 1554 1555 /** 1556 * Index content attachments as new entries in the idnex 1557 * @param collection the collection of attachments 1558 * @param content the content whose attachments will be indexed 1559 * @throws Exception if something goes wrong when indexing the attachments of the content 1560 */ 1561 public void indexContentAttachments(ResourceCollection collection, Content content) throws Exception 1562 { 1563 Request request = ContextHelper.getRequest(_context); 1564 String workspaceName = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1565 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1566 indexContentAttachments(collection, content, solrClient); 1567 } 1568 1569 /** 1570 * Index content attachments as new entries in the idnex 1571 * @param collection the collection of attachments 1572 * @param content the content whose attachments will be indexed 1573 * @param solrClient The solr client to use 1574 * @throws Exception if something goes wrong when indexing the attachments of the content 1575 */ 1576 public void indexContentAttachments(ResourceCollection collection, Content content, SolrClient solrClient) throws Exception 1577 { 1578 if (collection == null) 1579 { 1580 return; 1581 } 1582 1583 try (AmetysObjectIterable<AmetysObject> children = collection.getChildren()) 1584 { 1585 for (AmetysObject object : children) 1586 { 1587 if (object instanceof ResourceCollection) 1588 { 1589 indexContentAttachments((ResourceCollection) object, content, solrClient); 1590 } 1591 else if (object instanceof Resource) 1592 { 1593 Resource resource = (Resource) object; 1594 indexContentAttachment(resource, content, solrClient); 1595 } 1596 } 1597 } 1598 } 1599 1600 /** 1601 * Index a content attachment 1602 * @param resource the content attachment as a {@link Resource} 1603 * @param content the content whose attachment is going to be indexed 1604 * @throws Exception if something goes wrong when processing the indexation of the content attachment 1605 */ 1606 public void indexContentAttachment(Resource resource, Content content) throws Exception 1607 { 1608 Request request = ContextHelper.getRequest(_context); 1609 String workspaceName = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1610 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1611 indexContentAttachment(resource, content, solrClient); 1612 } 1613 1614 /** 1615 * Index a content attachment 1616 * @param resource the content attachment as a {@link Resource} 1617 * @param content the content whose attachment is going to be indexed 1618 * @param solrClient The solr client to use 1619 * @throws Exception if something goes wrong when processing the indexation of the content attachment 1620 */ 1621 public void indexContentAttachment(Resource resource, Content content, SolrClient solrClient) throws Exception 1622 { 1623 SolrInputDocument document = new SolrInputDocument(); 1624 1625 // Prepare resource doc 1626 _indexContentAttachment(resource, document, content); 1627 1628 // Indexation of the document 1629 _indexResourceDocument(resource, document, solrClient); 1630 } 1631 1632 private void _indexContentAttachment(Resource resource, SolrInputDocument document, Content content) throws Exception 1633 { 1634 String language = content.getLanguage(); 1635 1636 _solrResourceIndexer.indexResource(resource, document, SolrFieldNames.TYPE_CONTENT_ATTACHMENT_RESOURCE, language); 1637 1638 // Need the id of the content for unindexing attachment during the unindexing of the content 1639 document.addField(SolrFieldNames.ATTACHMENT_CONTENT_ID, content.getId()); 1640 } 1641 1642 /** 1643 * Index a populated solr input document of type Resource. 1644 * @param resource the resource from which the input document is created 1645 * @param document the input document 1646 * @param solrClient The solr client to use 1647 * @throws SolrServerException if there is an error on the server 1648 * @throws IOException if there is a communication error with the server 1649 */ 1650 protected void _indexResourceDocument(Resource resource, SolrInputDocument document, SolrClient solrClient) throws SolrServerException, IOException 1651 { 1652 String resourceId = resource.getId(); 1653 1654 // Retrieve appropriate collection name 1655 Request request = ContextHelper.getRequest(_context); 1656 String workspaceName = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1657 String collectionName = _solrClientProvider.getCollectionName(workspaceName); 1658 1659 // Add document 1660 UpdateResponse solrResponse = solrClient.add(collectionName, document); 1661 int status = solrResponse.getStatus(); 1662 1663 if (status != 0) 1664 { 1665 throw new IOException("Ametys resource indexing - Expecting status code of '0' in the Solr response but got : '" + status + "'. Resource id : " + resourceId); 1666 } 1667 1668 getLogger().debug("Successful resource indexing. Resource identifier : {}", resourceId); 1669 } 1670 1671 /** 1672 * Delete repeater documents of a specified content. 1673 * @param workspaceName The workspace name 1674 * @param contentId the content ID. 1675 * @param solrClient The solr client to use 1676 * @throws Exception if an error occurs while indexing. 1677 */ 1678 protected void deleteRepeaterDocs(String contentId, String workspaceName, SolrClient solrClient) throws Exception 1679 { 1680 long time_0 = System.currentTimeMillis(); 1681 1682 // _documentType:repeater AND id:content\://xxx/* 1683 StringBuilder query = new StringBuilder(); 1684 query.append(SolrFieldNames.DOCUMENT_TYPE).append(':').append(SolrFieldNames.TYPE_REPEATER) 1685 .append(" AND id:").append(ClientUtils.escapeQueryChars(contentId)).append("/*"); 1686 1687 solrClient.deleteByQuery(_solrClientProvider.getCollectionName(workspaceName), query.toString()); 1688 1689 getLogger().debug("Successfully delete repeaters documents for content {} in {} ms", contentId, System.currentTimeMillis() - time_0); 1690 } 1691 1692 /** 1693 * Index all the resources in a given workspace. 1694 * @param workspaceName The workspace where to index 1695 * @param solrClient The solr client to use 1696 * @param progressionTracker The progression of the indexation 1697 * @return The indexation result as a Map. 1698 * @throws Exception if an error occurs while indexing. 1699 */ 1700 public Map<String, Object> indexAllResources(String workspaceName, SolrClient solrClient, SimpleProgressionTracker progressionTracker) throws Exception 1701 { 1702 Request request = ContextHelper.getRequest(_context); 1703 1704 // Retrieve the current workspace. 1705 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1706 1707 try 1708 { 1709 // Force the workspace. 1710 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1711 1712 getLogger().info("Starting the indexation of all resources for workspace {}", workspaceName); 1713 1714 long start = System.currentTimeMillis(); 1715 1716 // Delete all resources 1717 _unindexAllResources(workspaceName, solrClient); 1718 1719 Map<String, Object> results = new HashMap<>(); 1720 1721 try 1722 { 1723 TraversableAmetysObject resourceRoot = _resolver.resolveByPath(RepositoryConstants.NAMESPACE_PREFIX + ":resources"); 1724 AmetysObjectIterable<Resource> resources = resourceRoot.getChildren(); 1725 1726 IndexationResult result = doIndexResources(resources, SolrFieldNames.TYPE_RESOURCE, resourceRoot, workspaceName, solrClient, progressionTracker); 1727 1728 long end = System.currentTimeMillis(); 1729 1730 if (!result.hasErrors()) 1731 { 1732 getLogger().info("{} resources indexed without error in {} milliseconds.", result.successCount(), end - start); 1733 } 1734 else 1735 { 1736 getLogger().info("Resource indexation ended, the process took {} milliseconds. {} resources were not indexed successfully, please review the error logs above for more details.", end - start, result.errorCount()); 1737 } 1738 1739 results.put("successCount", result.successCount()); 1740 if (result.hasErrors()) 1741 { 1742 results.put("errorCount", result.errorCount()); 1743 } 1744 } 1745 catch (UnknownAmetysObjectException e) 1746 { 1747 getLogger().info("There is no root for resources in current workspace."); 1748 progressionTracker.setSize(0); 1749 } 1750 1751 return results; 1752 } 1753 catch (Exception e) 1754 { 1755 String error = String.format("Failed to index all resources in workspace %s", workspaceName); 1756 getLogger().error(error, e); 1757 throw new IndexingException(error, e); 1758 } 1759 finally 1760 { 1761 // Restore context 1762 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1763 } 1764 } 1765 1766 /** 1767 * Send a collection of contents for indexation in the solr server. 1768 * @param resources the collection of contents to index. 1769 * @param documentType The document type of the resource 1770 * @param workspaceName The workspace where to index 1771 * @return the indexation result. 1772 * @throws Exception if an error occurs while indexing. 1773 */ 1774 public IndexationResult indexResources(Iterable<AmetysObject> resources, String documentType, String workspaceName) throws Exception 1775 { 1776 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1777 return indexResources(resources, documentType, null, workspaceName, solrClient); 1778 } 1779 1780 /** 1781 * Send a collection of contents for indexation in the solr server. 1782 * @param resources the collection of contents to index. 1783 * @param documentType The document type of the resource 1784 * @param resourceRoot The resource root, can be null. In that case, it will be computed for each resource. 1785 * @param workspaceName The workspace where to index 1786 * @param solrClient The solr client to use 1787 * @return the indexation result. 1788 * @throws Exception if an error occurs while indexing. 1789 */ 1790 public IndexationResult indexResources(Iterable<? extends AmetysObject> resources, String documentType, TraversableAmetysObject resourceRoot, String workspaceName, SolrClient solrClient) throws Exception 1791 { 1792 return indexResources(resources, documentType, resourceRoot, workspaceName, solrClient, ProgressionTrackerFactory.createSimpleProgressionTracker("Index resources", getLogger())); 1793 } 1794 1795 /** 1796 * Send a collection of contents for indexation in the solr server. 1797 * @param resources the collection of contents to index. 1798 * @param documentType The document type of the resource 1799 * @param resourceRoot The resource root, can be null. In that case, it will be computed for each resource. 1800 * @param workspaceName The workspace where to index 1801 * @param solrClient The solr client to use 1802 * @param progressionTracker The progression of the indexation 1803 * @return the indexation result. 1804 * @throws Exception if an error occurs while indexing. 1805 */ 1806 public IndexationResult indexResources(Iterable<? extends AmetysObject> resources, String documentType, TraversableAmetysObject resourceRoot, String workspaceName, SolrClient solrClient, SimpleProgressionTracker progressionTracker) throws Exception 1807 { 1808 Request request = ContextHelper.getRequest(_context); 1809 1810 // Retrieve the current workspace. 1811 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1812 1813 try 1814 { 1815 // Force the workspace. 1816 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1817 1818 getLogger().info("Starting indexation of several resources for workspace {}", workspaceName); 1819 1820 long start = System.currentTimeMillis(); 1821 1822 IndexationResult result = doIndexResources(resources, documentType, resourceRoot, workspaceName, solrClient, progressionTracker); 1823 1824 long end = System.currentTimeMillis(); 1825 1826 if (!result.hasErrors()) 1827 { 1828 getLogger().info("{} resources indexed without error in {} milliseconds.", result.successCount(), end - start); 1829 } 1830 else 1831 { 1832 getLogger().info("Resource indexation ended, the process took {} milliseconds. {} resources were not indexed successfully, please review the error logs above for more details.", end - start, result.errorCount()); 1833 } 1834 1835 return result; 1836 } 1837 catch (Exception e) 1838 { 1839 String error = String.format("Failed to index several resources in workspace %s", workspaceName); 1840 getLogger().error(error, e); 1841 throw new IndexingException(error, e); 1842 } 1843 finally 1844 { 1845 // Restore context 1846 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1847 } 1848 } 1849 1850 /** 1851 * Send some resources for indexation in the solr server. 1852 * @param resources the resources to index. 1853 * @param documentType The document type of the resource 1854 * @param resourceRoot The resource root, can be null. In that case, it will be computed for each resource. 1855 * @param workspaceName The workspace where to index 1856 * @param solrClient The solr client to use 1857 * @param progressionTracker The progression of the indexation 1858 * @return the indexation result. 1859 * @throws Exception if an error occurs committing the results. 1860 */ 1861 protected IndexationResult doIndexResources(Iterable<? extends AmetysObject> resources, String documentType, TraversableAmetysObject resourceRoot, String workspaceName, SolrClient solrClient, SimpleProgressionTracker progressionTracker) throws Exception 1862 { 1863 try 1864 { 1865 // Add callable for each content to index 1866 List<Future<Void>> tasks = new ArrayList<>(); 1867 for (AmetysObject resource : resources) 1868 { 1869 tasks.addAll(_asyncIndexExplorerItem(resource, documentType, resourceRoot, solrClient)); 1870 } 1871 1872 // Now that everything is submitted, we can iterate and wait for result 1873 return IndexationResult.fromTasks(tasks, getLogger()); 1874 } 1875 finally 1876 { 1877 progressionTracker.increment(); 1878 } 1879 } 1880 1881 /** 1882 * Add or update a resource into Solr index 1883 * @param resource The resource to index 1884 * @param documentType The document type of the resource 1885 * @param workspaceName The workspace where to index 1886 * @throws Exception if an error occurs while indexing. 1887 */ 1888 public void indexResource(Resource resource, String documentType, String workspaceName) throws Exception 1889 { 1890 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1891 doIndexResources(Collections.singleton(resource), documentType, null, workspaceName, solrClient, ProgressionTrackerFactory.createSimpleProgressionTracker("Index resource", getLogger())); 1892 } 1893 1894 private void _unindexAllResources(String workspaceName, SolrClient solrClient) throws Exception 1895 { 1896 getLogger().debug("Unindexing all resources from Solr."); 1897 1898 String collection = _solrClientProvider.getCollectionName(workspaceName); 1899 solrClient.deleteByQuery(collection, SolrFieldNames.DOCUMENT_TYPE + ':' + SolrFieldNames.TYPE_RESOURCE); 1900 1901 getLogger().debug("Succesfully deleted all resource documents from Solr."); 1902 } 1903 1904 /** 1905 * Delete all resource documents at a given path for all workspaces and commit 1906 * @param rootId The resource root ID, must not be null. 1907 * @param path The resource path relative to the given root, must start with a slash. 1908 * @throws Exception If an error occurs while unindexing. 1909 */ 1910 public void unindexResourcesByPath(String rootId, String path) throws Exception 1911 { 1912 String[] workspaceNames = _repository.getWorkspaces(); 1913 for (String workspaceName : workspaceNames) 1914 { 1915 unindexResourcesByPath(rootId, path, workspaceName); 1916 } 1917 } 1918 1919 /** 1920 * Delete all resource documents at a given path. 1921 * @param rootId The resource root ID, must not be null. 1922 * @param path The resource path relative to the given root, must start with a slash. 1923 * @param workspaceName The workspace where to work in 1924 * @throws Exception If an error occurs while unindexing. 1925 */ 1926 public void unindexResourcesByPath(String rootId, String path, String workspaceName) throws Exception 1927 { 1928 Request request = ContextHelper.getRequest(_context); 1929 1930 // Retrieve the current workspace. 1931 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1932 1933 try 1934 { 1935 // Force the workspace. 1936 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1937 1938 getLogger().debug("Unindexing all resources at path {} in root {}", path, rootId); 1939 1940 Query query = new ResourceLocationQuery(rootId, path); 1941 1942 String collection = _solrClientProvider.getCollectionName(workspaceName); 1943 _getAutoCommitSolrClient(workspaceName).deleteByQuery(collection, query.build()); 1944 1945 getLogger().debug("Succesfully deleted resource document from Solr."); 1946 } 1947 catch (Exception e) 1948 { 1949 String error = String.format("Failed to unindex resource %s in workspace %s", path, workspaceName); 1950 getLogger().error(error, e); 1951 throw new IndexingException(error, e); 1952 } 1953 finally 1954 { 1955 // Restore workspace 1956 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1957 } 1958 } 1959 1960 /** 1961 * Remove a resource from Solr index for all workspaces and commit 1962 * @param resourceId The id of resource to unindex 1963 * @throws Exception if an error occurs while indexing. 1964 */ 1965 public void unindexResource(String resourceId) throws Exception 1966 { 1967 String[] workspaceNames = _repository.getWorkspaces(); 1968 for (String workspaceName : workspaceNames) 1969 { 1970 unindexResource(resourceId, workspaceName); 1971 } 1972 } 1973 1974 /** 1975 * Remove a resource from the Solr index. 1976 * @param resourceId The id of resource to unindex 1977 * @param workspaceName The workspace where to work in 1978 * @throws Exception if an error occurs while unindexing. 1979 */ 1980 public void unindexResource(String resourceId, String workspaceName) throws Exception 1981 { 1982 Request request = ContextHelper.getRequest(_context); 1983 1984 // Retrieve the current workspace. 1985 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1986 1987 try 1988 { 1989 // Force the workspace. 1990 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1991 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1992 1993 getLogger().debug("Unindexing resource {} from Solr.", resourceId); 1994 1995 doUnindexDocument(resourceId, workspaceName, solrClient); 1996 1997 getLogger().debug("Succesfully deleted resource {} from Solr.", resourceId); 1998 } 1999 catch (Exception e) 2000 { 2001 String error = String.format("Failed to unindex resource %s in workspace %s", resourceId, workspaceName); 2002 getLogger().error(error, e); 2003 throw new IndexingException(error, e); 2004 } 2005 finally 2006 { 2007 // Restore workspace 2008 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 2009 } 2010 } 2011 2012 private List<Future<Void>> _asyncIndexExplorerItem(AmetysObject node, String documentType, TraversableAmetysObject resourceRoot, SolrClient solrClient) throws Exception 2013 { 2014 List<Future<Void>> tasks = new ArrayList<>(); 2015 2016 if (node instanceof ResourceCollection resourceCollection) 2017 { 2018 try (AmetysObjectIterable<AmetysObject> children = resourceCollection.getChildren()) 2019 { 2020 for (AmetysObject child : children) 2021 { 2022 tasks.addAll(_asyncIndexExplorerItem(child, documentType, resourceRoot, solrClient)); 2023 } 2024 } 2025 } 2026 else if (node instanceof Resource resource) 2027 { 2028 tasks.add(_threadIndexerHelper.submitCallable(new ResourceIndexerCallable(resource, _workspaceSelector.getWorkspace(), documentType, resourceRoot, solrClient))); 2029 } 2030 2031 return tasks; 2032 } 2033 2034 /** 2035 * Process a Solr commit operation in all workspaces. 2036 * <br>Use this only after a long operation with updates sent via {@link NoAutoCommitUpdateClient} 2037 * @throws SolrServerException if there is an error on the server 2038 * @throws IOException if there is a communication error with the server 2039 */ 2040 public void commit() throws SolrServerException, IOException 2041 { 2042 String[] workspaceNames; 2043 try 2044 { 2045 workspaceNames = _repository.getWorkspaces(); 2046 for (String workspaceName : workspaceNames) 2047 { 2048 SolrClient solrClient = _getNoAutoCommitSolrClient(workspaceName); 2049 commit(workspaceName, solrClient); 2050 } 2051 } 2052 catch (RepositoryException e) 2053 { 2054 throw new RuntimeException("An exception occured while retrieving JCR workspaces. Cannot commited to Solr.", e); 2055 } 2056 } 2057 2058 /** 2059 * Process a Solr commit operation in given workspace. 2060 * @param workspaceName The workspace's name 2061 * @param solrClient The solr client to use 2062 * @throws SolrServerException if there is an error on the server 2063 * @throws IOException if there is a communication error with the server 2064 */ 2065 public void commit(String workspaceName, SolrClient solrClient) throws SolrServerException, IOException 2066 { 2067 long time_0 = System.currentTimeMillis(); 2068 2069 // Commit 2070 UpdateResponse solrResponse = solrClient.commit(_solrClientProvider.getCollectionName(workspaceName)); 2071 int status = solrResponse.getStatus(); 2072 2073 if (status != 0) 2074 { 2075 throw new IOException("Ametys indexing: Solr commit operation - Expecting status code of '0' in the Solr response but got : '" + status + "'."); 2076 } 2077 2078 getLogger().debug("Successful Solr commit operation during an Ametys indexing process in {} ms", System.currentTimeMillis() - time_0); 2079 } 2080 2081 /** 2082 * Launch a solr index optimization. 2083 * @param workspaceName The workspace's name 2084 * @param solrClient The solr client to use 2085 * @throws SolrServerException if there is an error on the server 2086 * @throws IOException if there is a communication error with the server 2087 */ 2088 public void optimize(String workspaceName, SolrClient solrClient) throws SolrServerException, IOException 2089 { 2090 UpdateResponse solrResponse = solrClient.optimize(_solrClientProvider.getCollectionName(workspaceName)); 2091 int status = solrResponse.getStatus(); 2092 2093 if (status != 0) 2094 { 2095 throw new IOException("Ametys indexing: Solr optimize operation - Expecting status code of '0' in the Solr response but got : '" + status + "'."); 2096 } 2097 2098 getLogger().debug("Successful Solr optimize operation during an Ametys indexing process."); 2099 } 2100 2101 /** 2102 * Delete all documents from the solr index. 2103 * @param workspaceName The workspace name 2104 * @param solrClient The solr client to use 2105 * @throws Exception if an error occurs while unindexing. 2106 */ 2107 public void unindexAllDocuments(String workspaceName, SolrClient solrClient) throws Exception 2108 { 2109 getLogger().debug("Deleting all documents from Solr."); 2110 2111 String collection = _solrClientProvider.getCollectionName(workspaceName); 2112 solrClient.deleteByQuery(collection, "*:*"); 2113 2114 getLogger().debug("Successfully deleted all documents from Solr."); 2115 } 2116 2117 /** 2118 * Delete a document from the Solr server. 2119 * @param id The id of the document to delete from Solr 2120 * @param workspaceName The workspace name 2121 * @param solrClient The solr client to use 2122 * @throws Exception if an error occurs while indexing. 2123 */ 2124 protected void doUnindexDocument(String id, String workspaceName, SolrClient solrClient) throws Exception 2125 { 2126 UpdateResponse solrResponse = solrClient.deleteById(_solrClientProvider.getCollectionName(workspaceName), id); 2127 int status = solrResponse.getStatus(); 2128 2129 if (status != 0) 2130 { 2131 throw new IOException("Deletion of document " + id + ": got status code '" + status + "'."); 2132 } 2133 } 2134 2135 /** 2136 * Delete content attachments documents of a given content from the Solr server. 2137 * @param contentId The id of the content 2138 * @param workspaceName The workspace name 2139 * @param solrClient The solr client to use 2140 * @throws Exception if an error occurs while indexing. 2141 */ 2142 protected void doUnindexContentAttachments(String contentId, String workspaceName, SolrClient solrClient) throws Exception 2143 { 2144 String collectionName = _solrClientProvider.getCollectionName(workspaceName); 2145 2146 Query query = new ContentAttachmentQuery(contentId); 2147 UpdateResponse solrResponse = solrClient.deleteByQuery(collectionName, query.build()); 2148 int status = solrResponse.getStatus(); 2149 2150 if (status != 0) 2151 { 2152 throw new IOException("Deletion of content attachments of content " + contentId + ": got status code '" + status + "'."); 2153 } 2154 } 2155 2156 /** 2157 * Add or update a trash element into Solr index on all workspaces and commit 2158 * @param trashElementId The id of the trash element to index 2159 * @throws Exception if an error occurs while indexing. 2160 */ 2161 public void indexTrashElement(String trashElementId) throws Exception 2162 { 2163 String[] workspaceNames = _repository.getWorkspaces(); 2164 for (String workspaceName : workspaceNames) 2165 { 2166 indexTrashElement(trashElementId, workspaceName); 2167 } 2168 } 2169 2170 /** 2171 * Add or update a trash element into Solr index 2172 * @param trashElementId The id of the trash element to index 2173 * @param workspaceName the workspace where to index 2174 * @throws Exception if an error occurs while indexing. 2175 */ 2176 public void indexTrashElement(String trashElementId, String workspaceName) throws Exception 2177 { 2178 Request request = ContextHelper.getRequest(_context); 2179 2180 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 2181 2182 // Retrieve the current workspace. 2183 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 2184 2185 try 2186 { 2187 // Force the workspace. 2188 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 2189 2190 if (_resolver.hasAmetysObjectForId(trashElementId)) 2191 { 2192 DefaultTrashElement trashElement = _resolver.resolveById(trashElementId); 2193 doIndexTrashElements(Collections.singleton(trashElement), workspaceName, solrClient, ProgressionTrackerFactory.createSimpleProgressionTracker("Index trash element", getLogger())); 2194 } 2195 } 2196 finally 2197 { 2198 // Restore context 2199 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 2200 } 2201 } 2202 2203 /** 2204 * Index all the trash elements in a given workspace. 2205 * @param workspaceName the workspace where to index 2206 * @param solrClient The solr client to use 2207 * @param progressionTracker The progression of the indexation 2208 * @return The indexation result as a Map. 2209 * @throws Exception if an error occurs while indexing. 2210 */ 2211 public Map<String, Object> indexAllTrashElements(String workspaceName, SolrClient solrClient, SimpleProgressionTracker progressionTracker) throws Exception 2212 { 2213 Request request = ContextHelper.getRequest(_context); 2214 2215 // Retrieve the current workspace. 2216 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 2217 2218 try 2219 { 2220 // Force the workspace. 2221 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 2222 2223 getLogger().info("Starting the indexation of all trash elements for workspace {}", workspaceName); 2224 2225 long start = System.currentTimeMillis(); 2226 2227 // Delete all trash elements 2228 unindexAllTrashElements(workspaceName, solrClient); 2229 2230 String query = QueryHelper.getXPathQuery(null, TrashElementFactory.TRASH_ELEMENT_NODETYPE, null); 2231 AmetysObjectIterable<DefaultTrashElement> trashElements = _resolver.query(query); 2232 2233 IndexationResult result = doIndexTrashElements(trashElements, workspaceName, solrClient, progressionTracker); 2234 2235 long end = System.currentTimeMillis(); 2236 2237 if (!result.hasErrors()) 2238 { 2239 getLogger().info("{} trash elements indexed without error in {} milliseconds.", result.successCount(), end - start); 2240 } 2241 else 2242 { 2243 getLogger().info("Trash elements indexation ended, the process took {} milliseconds. {} trash elements were not indexed successfully, please review the error logs above for more details.", end - start, result.errorCount()); 2244 } 2245 2246 Map<String, Object> results = new HashMap<>(); 2247 results.put("successCount", result.successCount()); 2248 if (result.hasErrors()) 2249 { 2250 results.put("errorCount", result.errorCount()); 2251 } 2252 2253 return results; 2254 } 2255 catch (Exception e) 2256 { 2257 String error = String.format("Failed to index all trash elements in workspace %s", workspaceName); 2258 getLogger().error(error, e); 2259 throw new IndexingException(error, e); 2260 } 2261 finally 2262 { 2263 // Restore context 2264 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 2265 } 2266 } 2267 2268 /** 2269 * Remove a trash element from Solr index for all workspaces and commit 2270 * @param trashElementId The id of trash element to unindex 2271 * @throws Exception if an error occurs while indexing. 2272 */ 2273 public void unindexTrashElement(String trashElementId) throws Exception 2274 { 2275 String[] workspaceNames = _repository.getWorkspaces(); 2276 for (String workspaceName : workspaceNames) 2277 { 2278 unindexTrashElement(trashElementId, workspaceName); 2279 } 2280 } 2281 2282 /** 2283 * Remove a trash element from Solr index 2284 * @param trashElementId The id of trash element to unindex 2285 * @param workspaceName The workspace where to work in 2286 * @throws Exception if an error occurs while indexing. 2287 */ 2288 public void unindexTrashElement(String trashElementId, String workspaceName) throws Exception 2289 { 2290 Request request = ContextHelper.getRequest(_context); 2291 2292 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 2293 2294 // Retrieve the current workspace. 2295 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 2296 2297 try 2298 { 2299 // Force the workspace. 2300 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 2301 2302 UpdateResponse solrResponse = solrClient.deleteById(_solrClientProvider.getCollectionName(workspaceName), trashElementId); 2303 int status = solrResponse.getStatus(); 2304 2305 if (status != 0) 2306 { 2307 throw new IOException("Deletion of document " + trashElementId + ": got status code '" + status + "'."); 2308 } 2309 } 2310 finally 2311 { 2312 // Restore context 2313 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 2314 } 2315 } 2316 2317 /** 2318 * Unindex all trash elements documents. 2319 * @param workspaceName The workspace name 2320 * @param solrClient The solr client to use 2321 * @throws Exception if an error occurs while unindexing. 2322 */ 2323 protected void unindexAllTrashElements(String workspaceName, SolrClient solrClient) throws Exception 2324 { 2325 getLogger().debug("Unindexing all trash elements from Solr."); 2326 2327 String collection = _solrClientProvider.getCollectionName(workspaceName); 2328 Query query = new DocumentTypeQuery(SolrFieldNames.TYPE_TRASH_ELEMENT); 2329 solrClient.deleteByQuery(collection, query.build()); 2330 2331 getLogger().debug("Succesfully deleted all trash elements documents from Solr."); 2332 } 2333 2334 /** 2335 * Send some trash elements for indexation in the solr server. 2336 * @param trashElements the trash elements to index. 2337 * @param workspaceName The workspace name 2338 * @param solrClient The solr client to use 2339 * @param progressionTracker The progression of the indexation 2340 * @return the indexation result. 2341 * @throws Exception if an error occurs committing the results. 2342 */ 2343 protected IndexationResult doIndexTrashElements(Iterable<DefaultTrashElement> trashElements, String workspaceName, SolrClient solrClient, SimpleProgressionTracker progressionTracker) throws Exception 2344 { 2345 int numberOfTrashElements = IterableUtils.size(trashElements); 2346 progressionTracker.setSize(numberOfTrashElements); 2347 2348 // Add callable for each content to index 2349 List<Future<Void>> tasks = new ArrayList<>(); 2350 for (DefaultTrashElement trashElement : trashElements) 2351 { 2352 tasks.add(_threadIndexerHelper.submitCallable(new TrashElementIndexerCallable(trashElement, workspaceName, solrClient, progressionTracker))); 2353 } 2354 2355 // Now that everything is submitted, we can iterate and wait for result 2356 return IndexationResult.fromTasks(tasks, getLogger()); 2357 } 2358 2359// /** 2360// * Get the collection to use. 2361// * @return The name of the collection to index into. 2362// */ 2363// protected String getCollection() 2364// { 2365// return _solrCorePrefix + _workspaceSelector.getWorkspace(); 2366// } 2367 2368 private static final class SchemaRequestComparator implements Comparator<SchemaRequest.Update> 2369 { 2370 public int compare(SchemaRequest.Update u1, SchemaRequest.Update u2) 2371 { 2372 return Integer.compare(_getOrder(u1), _getOrder(u2)); 2373 } 2374 2375 private int _getOrder(SchemaRequest.Update update) 2376 { 2377 // Field types are needed first to define fields 2378 if (update instanceof SchemaRequest.AddFieldType) 2379 { 2380 return 1; 2381 } 2382 2383 // Fields or dynamic fields can be defined at the same time 2384 if (update instanceof SchemaRequest.AddField || update instanceof SchemaRequest.AddDynamicField) 2385 { 2386 return 2; 2387 } 2388 2389 // Copy fields can be based on fields or dynamic fields 2390 if (update instanceof SchemaRequest.AddCopyField) 2391 { 2392 return 3; 2393 } 2394 2395 return 0; 2396 } 2397 } 2398 2399 private class ContentIndexerCallable extends AbstractIndexerCallable<Content> 2400 { 2401 private boolean _indexAttachments; 2402 private SimpleProgressionTracker _tracker; 2403 2404 @SuppressWarnings("synthetic-access") 2405 public ContentIndexerCallable(Content content, String workspaceName, boolean indexAttachments, SolrClient solrClient, SimpleProgressionTracker progressionTracker) 2406 { 2407 super(content, workspaceName, solrClient, _manager, _cocoonContext, _resolver, getLogger()); 2408 this._indexAttachments = indexAttachments; 2409 this._tracker = progressionTracker; 2410 } 2411 2412 @Override 2413 protected void process(Content content) throws Exception 2414 { 2415 try 2416 { 2417 doIndexContent(content, _workspaceName, _solrClient); 2418 doIndexContentWorkflow(content, _workspaceName, _solrClient); 2419 if (_indexAttachments) 2420 { 2421 indexContentAttachments(content.getRootAttachments(), content, _solrClient); 2422 } 2423 } 2424 finally 2425 { 2426 _tracker.increment(); 2427 } 2428 } 2429 2430 @Override 2431 protected String getObjectLabel() 2432 { 2433 return "content"; 2434 } 2435 } 2436 2437 private class ResourceIndexerCallable extends AbstractIndexerCallable<Resource> 2438 { 2439 private String _documentType; 2440 private TraversableAmetysObject _resourceRoot; 2441 2442 @SuppressWarnings("synthetic-access") 2443 public ResourceIndexerCallable(Resource resource, String workspaceName, String documentType, TraversableAmetysObject resourceRoot, SolrClient solrClient) 2444 { 2445 super(resource, workspaceName, solrClient, _manager, _cocoonContext, _resolver, getLogger()); 2446 this._documentType = documentType; 2447 this._resourceRoot = resourceRoot; 2448 } 2449 2450 @Override 2451 protected void process(Resource resource) throws Exception 2452 { 2453 SolrInputDocument document = new SolrInputDocument(); 2454 2455 _solrResourceIndexer.indexResource(resource, document, _documentType, _resourceRoot); 2456 2457 UpdateResponse solrResponse = _solrClient.add(_solrClientProvider.getCollectionName(_workspaceName), document); 2458 int status = solrResponse.getStatus(); 2459 2460 if (status != 0) 2461 { 2462 throw new IOException("Resource indexation: got status code '" + status + "'."); 2463 } 2464 } 2465 2466 @Override 2467 protected String getObjectLabel() 2468 { 2469 return "resource"; 2470 } 2471 } 2472 2473 private class TrashElementIndexerCallable extends AbstractIndexerCallable<DefaultTrashElement> 2474 { 2475 private SimpleProgressionTracker _tracker; 2476 2477 @SuppressWarnings("synthetic-access") 2478 public TrashElementIndexerCallable(DefaultTrashElement trashElement, String workspaceName, SolrClient solrClient, SimpleProgressionTracker progressionTracker) 2479 { 2480 super(trashElement, workspaceName, solrClient, _manager, _cocoonContext, _resolver, getLogger()); 2481 this._tracker = progressionTracker; 2482 } 2483 2484 @Override 2485 protected void process(DefaultTrashElement trashElement) throws Exception 2486 { 2487 try 2488 { 2489 SolrInputDocument document = new SolrInputDocument(); 2490 _solrTrashElementIndexer.indexTrashElement(trashElement, document); 2491 2492 UpdateResponse solrResponse = _solrClient.add(_solrClientProvider.getCollectionName(_workspaceName), document); 2493 int status = solrResponse.getStatus(); 2494 2495 if (status != 0) 2496 { 2497 throw new IOException("Trash element indexation: got status code '" + status + "'."); 2498 } 2499 } 2500 finally 2501 { 2502 _tracker.increment(); 2503 } 2504 } 2505 2506 @Override 2507 protected String getObjectLabel() 2508 { 2509 return "trashElement"; 2510 } 2511 } 2512}