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 getLogger().info("Read-ACL Solr cache updated for workspace '{}' and objects {}", workspaceName, solrParams.keySet()); 831 } 832 else 833 { 834 @SuppressWarnings("unchecked") 835 List<String> unHandledObjects = (List<String>) responseObj.get("unhandled-objects"); 836 getLogger().info("The updating of Read-ACL Solr Cache for workspace '{}' did not succeed as expected.\n Following objects have not been updated: {}", workspaceName, unHandledObjects); 837 } 838 } 839 840 /** 841 * Index all the contents in a given workspace. 842 * @param workspaceName the workspace where to index 843 * @param indexAttachments to index content attachments 844 * @param solrClient The solr client to use 845 * @return The indexation result as a Map. 846 * @throws Exception if an error occurs while indexing. 847 */ 848 public Map<String, Object> indexAllContents(String workspaceName, boolean indexAttachments, SolrClient solrClient) throws Exception 849 { 850 return indexAllContents(workspaceName, indexAttachments, solrClient, ProgressionTrackerFactory.createSimpleProgressionTracker("Index all contents", getLogger())); 851 } 852 853 /** 854 * Index all the contents in a given workspace. 855 * @param workspaceName the workspace where to index 856 * @param indexAttachments to index content attachments 857 * @param solrClient The solr client to use 858 * @param progressionTracker The progression of the indexation 859 * @return The indexation result as a Map. 860 * @throws Exception if an error occurs while indexing. 861 */ 862 public Map<String, Object> indexAllContents(String workspaceName, boolean indexAttachments, SolrClient solrClient, SimpleProgressionTracker progressionTracker) throws Exception 863 { 864 Request request = ContextHelper.getRequest(_context); 865 866 // Retrieve the current workspace. 867 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 868 869 try 870 { 871 // Force the workspace. 872 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 873 874 getLogger().info("Starting the indexation of all contents for workspace {}", workspaceName); 875 876 long start = System.currentTimeMillis(); 877 878 // Delete all contents 879 unindexAllContents(workspaceName, indexAttachments, solrClient); 880 881 String query = ContentQueryHelper.getContentXPathQuery(null); 882 AmetysObjectIterable<Content> contents = _resolver.query(query); 883 884 IndexationResult result = doIndexContents(contents, workspaceName, indexAttachments, solrClient, progressionTracker); 885 886 long end = System.currentTimeMillis(); 887 888 if (!result.hasErrors()) 889 { 890 getLogger().info("{} contents indexed without error in {} milliseconds.", result.successCount(), end - start); 891 } 892 else 893 { 894 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()); 895 } 896 897 Map<String, Object> results = new HashMap<>(); 898 results.put("successCount", result.successCount()); 899 if (result.hasErrors()) 900 { 901 results.put("errorCount", result.errorCount()); 902 } 903 904 return results; 905 } 906 catch (Exception e) 907 { 908 String error = String.format("Failed to index all contents in workspace %s", workspaceName); 909 getLogger().error(error, e); 910 throw new IndexingException(error, e); 911 } 912 finally 913 { 914 // Restore context 915 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 916 } 917 } 918 919 /** 920 * Unindex all content documents. 921 * @param workspaceName The workspace name 922 * @param unindexAttachments also unindex content attachments 923 * @param solrClient The solr client to use 924 * @throws Exception if an error occurs while unindexing. 925 */ 926 protected void unindexAllContents(String workspaceName, boolean unindexAttachments, SolrClient solrClient) throws Exception 927 { 928 String collection = _solrClientProvider.getCollectionName(workspaceName); 929 930 Query contents = new DocumentTypeQuery(SolrFieldNames.TYPE_CONTENT); 931 Query query; 932 if (unindexAttachments) 933 { 934 Query contentResourceAttachments = new DocumentTypeQuery(SolrFieldNames.TYPE_CONTENT_ATTACHMENT_RESOURCE); 935 Query contentResourceAttributes = new DocumentTypeQuery(SolrFieldNames.TYPE_CONTENT_ATTRIBUTE_RESOURCE); 936 query = new OrQuery(contentResourceAttachments, contentResourceAttributes, contents); 937 } 938 else 939 { 940 query = contents; 941 } 942 solrClient.deleteByQuery(collection, query.build()); 943 } 944 945 /** 946 * Add or update the child contents of a {@link AmetysObjectCollection} into Solr index, for all workspaces and commit 947 * @param collectionId The id of collection 948 * @param indexAttachments to index content attachments 949 * @throws Exception if an error occurs while indexing. 950 */ 951 public void indexSubcontents(String collectionId, boolean indexAttachments) throws Exception 952 { 953 String[] workspaceNames = _repository.getWorkspaces(); 954 for (String workspaceName : workspaceNames) 955 { 956 indexSubcontents(collectionId, workspaceName, indexAttachments); 957 } 958 } 959 960 /** 961 * Index the child contents of a {@link AmetysObjectCollection} 962 * @param collectionId The id of collection 963 * @param workspaceName the workspace where to index 964 * @param indexAttachments to index content attachments 965 * @throws Exception if an error occurs while unindexing. 966 */ 967 public void indexSubcontents(String collectionId, String workspaceName, boolean indexAttachments) throws Exception 968 { 969 Request request = ContextHelper.getRequest(_context); 970 971 // Retrieve the current workspace. 972 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 973 974 try 975 { 976 // Force the workspace. 977 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 978 979 if (_resolver.hasAmetysObjectForId(collectionId)) 980 { 981 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 982 AmetysObjectCollection collection = _resolver.resolveById(collectionId); 983 AmetysObjectIterable<AmetysObject> children = collection.getChildren(); 984 985 for (AmetysObject child : children) 986 { 987 if (child instanceof Content) 988 { 989 Content content = (Content) child; 990 991 _doIndexContent(content, workspaceName, indexAttachments, solrClient); 992 } 993 } 994 } 995 } 996 finally 997 { 998 // Restore context 999 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1000 } 1001 } 1002 1003 /** 1004 * Add or update a content into Solr index on all workspaces and commit 1005 * @param contentId The id of the content to index 1006 * @param indexAttachments to index content attachments 1007 * @throws Exception if an error occurs while indexing. 1008 */ 1009 public void indexContent(String contentId, boolean indexAttachments) throws Exception 1010 { 1011 String[] workspaceNames = _repository.getWorkspaces(); 1012 for (String workspaceName : workspaceNames) 1013 { 1014 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1015 indexContent(contentId, workspaceName, indexAttachments, solrClient); 1016 } 1017 } 1018 1019 /** 1020 * Add or update a content into Solr index 1021 * @param contentId The id of the content to index 1022 * @param workspaceName the workspace where to index 1023 * @param indexAttachments to index content attachments 1024 * @throws Exception if an error occurs while indexing. 1025 */ 1026 public void indexContent(String contentId, String workspaceName, boolean indexAttachments) throws Exception 1027 { 1028 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1029 indexContent(contentId, workspaceName, indexAttachments, solrClient); 1030 } 1031 1032 /** 1033 * Add or update a content into Solr index 1034 * @param contentId The id of the content to index 1035 * @param workspaceName the workspace where to index 1036 * @param indexAttachments to index content attachments 1037 * @param solrClient The solr client to use 1038 * @throws Exception if an error occurs while indexing. 1039 */ 1040 public void indexContent(String contentId, String workspaceName, boolean indexAttachments, SolrClient solrClient) throws Exception 1041 { 1042 Request request = ContextHelper.getRequest(_context); 1043 1044 // Retrieve the current workspace. 1045 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1046 1047 try 1048 { 1049 // Force the workspace. 1050 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1051 1052 if (_resolver.hasAmetysObjectForId(contentId)) 1053 { 1054 Content content = _resolver.resolveById(contentId); 1055 _doIndexContent(content, workspaceName, indexAttachments, solrClient); 1056 } 1057 } 1058 finally 1059 { 1060 // Restore context 1061 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1062 } 1063 } 1064 1065 private void _doIndexContent(Content content, String workspaceName, boolean indexAttachments, SolrClient solrClient) throws IndexingException 1066 { 1067 try 1068 { 1069 long time_0 = System.currentTimeMillis(); 1070 1071 getLogger().debug("Indexing content {} into Solr for workspace {}", content.getId(), workspaceName); 1072 1073 deleteRepeaterDocs(content.getId(), workspaceName, solrClient); 1074 doIndexContent(content, workspaceName, solrClient); 1075 doIndexContentWorkflow(content, workspaceName, solrClient); 1076 if (indexAttachments) 1077 { 1078 indexContentAttachments(content.getRootAttachments(), content, solrClient); 1079 } 1080 1081 getLogger().debug("Successfully indexed content {} in Solr in {} ms", content.getId(), System.currentTimeMillis() - time_0); 1082 } 1083 catch (Exception e) 1084 { 1085 String error = String.format("Failed to index content %s in workspace %s", content.getId(), workspaceName); 1086 getLogger().error(error, e); 1087 throw new IndexingException(error, e); 1088 } 1089 } 1090 1091 /** 1092 * Send a collection of contents for indexation in the solr server on all workspaces and commit 1093 * @param contents the collection of contents to index. 1094 * @return the indexation result. 1095 * @throws Exception if an error occurs while indexing. 1096 */ 1097 public IndexationResult indexContents(Iterable<Content> contents) throws Exception 1098 { 1099 return indexContents(contents, ProgressionTrackerFactory.createContainerProgressionTracker("Index contents", getLogger())); 1100 } 1101 1102 1103 /** 1104 * Send a collection of contents for indexation in the solr server on all workspaces and commit 1105 * @param contents the collection of contents to index. 1106 * @param progressionTracker The progression of the indexation 1107 * @return the indexation result. 1108 * @throws Exception if an error occurs while indexing. 1109 */ 1110 public IndexationResult indexContents(Iterable<Content> contents, ContainerProgressionTracker progressionTracker) throws Exception 1111 { 1112 IndexationResult result = new IndexationResult(0, 0); 1113 1114 String[] workspaceNames = _repository.getWorkspaces(); 1115 1116 for (String workspaceName : workspaceNames) 1117 { 1118 progressionTracker.addSimpleStep(workspaceName, new I18nizableText("plugin.cms", "PLUGINS_CMS_SCHEDULER_GLOBAL_INDEXATION_CONTENT_WORKSPACE_STEP_LABEL", List.of(workspaceName))); 1119 } 1120 1121 for (String workspaceName : workspaceNames) 1122 { 1123 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1124 IndexationResult wResult = indexContents(contents, workspaceName, true, solrClient, progressionTracker.getStep(workspaceName)); 1125 1126 result = new IndexationResult(result.successCount() + wResult.successCount(), result.errorCount() + wResult.errorCount()); 1127 } 1128 1129 return result; 1130 } 1131 1132 /** 1133 * Send a collection of contents for indexation in the solr server. 1134 * @param contents the collection of contents to index. 1135 * @param workspaceName the workspace where to index 1136 * @param indexAttachments to index content attachments 1137 * @param solrClient The solr client to use 1138 * @return the indexation result. 1139 * @throws Exception if an error occurs while indexing. 1140 */ 1141 public IndexationResult indexContents(Iterable<Content> contents, String workspaceName, boolean indexAttachments, SolrClient solrClient) throws Exception 1142 { 1143 return indexContents(contents, workspaceName, indexAttachments, solrClient, ProgressionTrackerFactory.createSimpleProgressionTracker("Index contents for workspace " + workspaceName, getLogger())); 1144 } 1145 1146 /** 1147 * Send a collection of contents for indexation in the solr server. 1148 * @param contents the collection of contents to index. 1149 * @param workspaceName the workspace where to index 1150 * @param indexAttachments to index content attachments 1151 * @param solrClient The solr client to use 1152 * @param progressionTracker The progression of the indexation 1153 * @return the indexation result. 1154 * @throws Exception if an error occurs while indexing. 1155 */ 1156 public IndexationResult indexContents(Iterable<Content> contents, String workspaceName, boolean indexAttachments, SolrClient solrClient, SimpleProgressionTracker progressionTracker) throws Exception 1157 { 1158 Request request = ContextHelper.getRequest(_context); 1159 1160 // Retrieve the current workspace. 1161 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1162 1163 try 1164 { 1165 // Force the workspace. 1166 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1167 1168 getLogger().info("Starting indexation of several contents for workspace {}", workspaceName); 1169 1170 long start = System.currentTimeMillis(); 1171 1172 List<Content> contentsInWorkspace = StreamSupport.stream(contents.spliterator(), false) 1173 .map(Content::getId) 1174 .map(this::_resolveSilently) 1175 .filter(Objects::nonNull) 1176 .collect(Collectors.toList()); 1177 1178 IndexationResult result = doIndexContents(contentsInWorkspace, workspaceName, indexAttachments, solrClient, progressionTracker); 1179 1180 long end = System.currentTimeMillis(); 1181 1182 if (!result.hasErrors()) 1183 { 1184 getLogger().info("{} contents indexed without error in {} milliseconds.", result.successCount(), end - start); 1185 } 1186 else 1187 { 1188 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()); 1189 } 1190 1191 return result; 1192 } 1193 catch (Exception e) 1194 { 1195 String error = String.format("Failed to index several contents in workspace %s", workspaceName); 1196 getLogger().error(error, e); 1197 throw new IndexingException(error, e); 1198 } 1199 finally 1200 { 1201 // Restore context 1202 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1203 } 1204 } 1205 1206 private Content _resolveSilently(String contentId) 1207 { 1208 try 1209 { 1210 return _resolver.resolveById(contentId); 1211 } 1212 catch (UnknownAmetysObjectException e) 1213 { 1214 return null; 1215 } 1216 } 1217 1218 /** 1219 * Send some contents for indexation in the solr server. 1220 * @param contents the contents to index. 1221 * @param workspaceName The workspace name 1222 * @param indexAttachments to index content attachments 1223 * @param solrClient The solr client to use 1224 * @param progressionTracker The progression of the indexation 1225 * @return the indexation result. 1226 * @throws Exception if an error occurs committing the results. 1227 */ 1228 protected IndexationResult doIndexContents(Iterable<Content> contents, String workspaceName, boolean indexAttachments, SolrClient solrClient, SimpleProgressionTracker progressionTracker) throws Exception 1229 { 1230 int numberOfContents = IterableUtils.size(contents); 1231 progressionTracker.setSize(numberOfContents); 1232 1233 // Add callable for each content to index 1234 List<Future<Void>> tasks = new ArrayList<>(); 1235 for (Content content : contents) 1236 { 1237 tasks.add(_threadIndexerHelper.submitCallable(new ContentIndexerCallable(content, workspaceName, indexAttachments, solrClient, progressionTracker))); 1238 } 1239 1240 // Now that everything is submitted, we can iterate and wait for result 1241 return IndexationResult.fromTasks(tasks, getLogger()); 1242 } 1243 1244 /** 1245 * Update the value of a specific system property in a content document. 1246 * @param content The content to update. 1247 * @param propertyId The system property ID. 1248 * @param workspaceName The workspace name 1249 * @throws Exception if an error occurs while indexing. 1250 */ 1251 public void updateSystemProperty(Content content, String propertyId, String workspaceName) throws Exception 1252 { 1253 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1254 updateSystemProperty(content, propertyId, workspaceName, solrClient); 1255 } 1256 1257 /** 1258 * Update the value of a specific system property in a content document. 1259 * @param content The content to update. 1260 * @param propertyId The system property ID. 1261 * @param workspaceName The workspace name 1262 * @param solrClient The solr client to use 1263 * @throws Exception if an error occurs while indexing. 1264 */ 1265 public void updateSystemProperty(Content content, String propertyId, String workspaceName, SolrClient solrClient) throws Exception 1266 { 1267 getLogger().debug("Updating the system property '{}' for content {} into Solr.", propertyId, content); 1268 1269 SolrInputDocument document = new SolrInputDocument(); 1270 boolean hasUpdate = _solrContentIndexer.indexPartialSystemProperty(content, propertyId, document); 1271 1272 if (!hasUpdate) 1273 { 1274 getLogger().debug("Did not index '{}' system property for content {} in Solr because no update to apply.", propertyId, content); 1275 return; 1276 } 1277 1278 int status = _pushSolrDocument(document, workspaceName, solrClient); 1279 if (status != 0) 1280 { 1281 throw new IOException("Indexing of system property '" + propertyId + "': got status code '" + status + "'."); 1282 } 1283 1284 getLogger().debug("Succesfully indexed '{}' system property for content {} in Solr.", propertyId, content); 1285 } 1286 1287 /** 1288 * Update the value of a specific property in a content document. 1289 * @param content The content to update. 1290 * @param propertyId The system property ID. 1291 * @param workspaceName The workspace name 1292 * @throws Exception if an error occurs while indexing. 1293 */ 1294 public void updateProperty(Content content, String propertyId, String workspaceName) throws Exception 1295 { 1296 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1297 updateProperty(content, propertyId, workspaceName, solrClient); 1298 } 1299 1300 /** 1301 * Update the value of a specific property in a content document. 1302 * @param content The content to update. 1303 * @param propertyId The system property ID. 1304 * @param workspaceName The workspace name 1305 * @param solrClient The solr client to use 1306 * @throws Exception if an error occurs while indexing. 1307 */ 1308 public void updateProperty(Content content, String propertyId, String workspaceName, SolrClient solrClient) throws Exception 1309 { 1310 getLogger().debug("Updating the property '{}' for content {} into Solr.", propertyId, content); 1311 1312 SolrInputDocument document = new SolrInputDocument(); 1313 boolean hasUpdate = _solrContentIndexer.indexPartialProperty(content, propertyId, document); 1314 1315 if (!hasUpdate) 1316 { 1317 getLogger().debug("Did not index '{}' property for content {} in Solr because no update to apply.", propertyId, content); 1318 return; 1319 } 1320 1321 int status = _pushSolrDocument(document, workspaceName, solrClient); 1322 if (status != 0) 1323 { 1324 throw new IOException("Indexing of property '" + propertyId + "': got status code '" + status + "'."); 1325 } 1326 1327 getLogger().debug("Succesfully indexed '{}' property for content {} in Solr.", propertyId, content); 1328 } 1329 1330 private int _pushSolrDocument(SolrInputDocument document, String workspaceName, SolrClient solrClient) throws Exception 1331 { 1332 String collection = _solrClientProvider.getCollectionName(workspaceName); 1333 UpdateResponse solrResponse = solrClient.add(collection, document); 1334 return solrResponse.getStatus(); 1335 } 1336 1337 /** 1338 * Remove a content from Solr index for all workspaces and commit 1339 * @param contentId The id of content to unindex 1340 * @param unindexAttachments also unindex content attachments 1341 * @throws Exception if an error occurs while indexing. 1342 */ 1343 public void unindexContent(String contentId, boolean unindexAttachments) throws Exception 1344 { 1345 String[] workspaceNames = _repository.getWorkspaces(); 1346 for (String workspaceName : workspaceNames) 1347 { 1348 unindexContent(contentId, workspaceName, unindexAttachments); 1349 } 1350 } 1351 1352 /** 1353 * Remove a content from Solr index 1354 * @param contentId The id of content to unindex 1355 * @param workspaceName The workspace where to work in 1356 * @param unindexAttachments also unindex content attachments 1357 * @throws Exception if an error occurs while indexing. 1358 */ 1359 public void unindexContent(String contentId, String workspaceName, boolean unindexAttachments) throws Exception 1360 { 1361 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1362 unindexContent(contentId, workspaceName, unindexAttachments, solrClient); 1363 } 1364 1365 /** 1366 * Remove a content from Solr index 1367 * @param contentId The id of content to unindex 1368 * @param workspaceName The workspace where to work in 1369 * @param unindexAttachments also unindex content attachments 1370 * @param solrClient The solr client to use 1371 * @throws Exception if an error occurs while indexing. 1372 */ 1373 public void unindexContent(String contentId, String workspaceName, boolean unindexAttachments, SolrClient solrClient) throws Exception 1374 { 1375 Request request = ContextHelper.getRequest(_context); 1376 1377 // Retrieve the current workspace. 1378 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1379 1380 try 1381 { 1382 // Force the workspace. 1383 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1384 1385 getLogger().debug("Unindexing content {} from Solr for workspace {}", contentId, workspaceName); 1386 1387 deleteRepeaterDocs(contentId, workspaceName, solrClient); 1388 doUnindexDocument(contentId, workspaceName, solrClient); 1389 if (unindexAttachments) 1390 { 1391 doUnindexContentAttachments(contentId, workspaceName, solrClient); 1392 } 1393 _solrWorkflowIndexer.unindexAmetysObjectWorkflow(contentId, workspaceName, solrClient); 1394 1395 getLogger().debug("Succesfully deleted content {} from Solr.", contentId); 1396 } 1397 catch (Exception e) 1398 { 1399 String error = String.format("Failed to unindex content %s in workspace %s", contentId, workspaceName); 1400 getLogger().error(error, e); 1401 throw new IndexingException(error, e); 1402 } 1403 finally 1404 { 1405 // Restore workspace 1406 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1407 } 1408 } 1409 1410 /** 1411 * Remove a content from Solr index for all workspaces and commit 1412 * @param contentIds The id of content to unindex 1413 * @throws Exception if an error occurs while indexing. 1414 */ 1415 public void unindexContents(Collection<String> contentIds) throws Exception 1416 { 1417 String[] workspaceNames = _repository.getWorkspaces(); 1418 for (String workspaceName : workspaceNames) 1419 { 1420 unindexContents(contentIds, workspaceName); 1421 } 1422 } 1423 1424 /** 1425 * Remove a content from Solr index 1426 * @param contentIds The id of content to unindex 1427 * @param workspaceName The workspace where to work in 1428 * @throws Exception if an error occurs while indexing. 1429 */ 1430 public void unindexContents(Collection<String> contentIds, String workspaceName) throws Exception 1431 { 1432 Request request = ContextHelper.getRequest(_context); 1433 1434 // Retrieve the current workspace. 1435 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1436 1437 try 1438 { 1439 // Force the workspace. 1440 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1441 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1442 1443 getLogger().debug("Unindexing several contents from Solr."); 1444 1445 for (String contentId : contentIds) 1446 { 1447 deleteRepeaterDocs(contentId, workspaceName, solrClient); 1448 doUnindexDocument(contentId, workspaceName, solrClient); 1449 _solrWorkflowIndexer.unindexAmetysObjectWorkflow(contentId, workspaceName, solrClient); 1450 } 1451 1452 getLogger().debug("Succesfully unindexed content from Solr."); 1453 } 1454 catch (Exception e) 1455 { 1456 String error = String.format("Failed to unindex several contents in workspace %s", workspaceName); 1457 getLogger().error(error, e); 1458 throw new IndexingException(error, e); 1459 } 1460 finally 1461 { 1462 // Restore workspace 1463 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1464 } 1465 } 1466 1467 /** 1468 * Add or update a content into Solr index 1469 * @param content The content to index 1470 * @param workspaceName The workspace where to index 1471 * @param solrClient The solr client to use 1472 * @throws Exception if an error occurs while indexing. 1473 */ 1474 protected void doIndexContent(Content content, String workspaceName, SolrClient solrClient) throws Exception 1475 { 1476 long time_0 = System.currentTimeMillis(); 1477 1478 SolrInputDocument document = new SolrInputDocument(); 1479 List<SolrInputDocument> additionalDocuments = _solrContentIndexer.indexContent(content, document); 1480 1481 long time_1 = System.currentTimeMillis(); 1482 getLogger().debug("Populate indexing fields for content {} for workspace {} in {} ms", content.getId(), workspaceName, time_1 - time_0); 1483 1484 indexAclInitValues(content, document); 1485 1486 long time_2 = System.currentTimeMillis(); 1487 getLogger().debug("Populate ACL for content {} for workspace {} in {} ms", content.getId(), workspaceName, time_2 - time_1); 1488 1489 List<SolrInputDocument> documents = new ArrayList<>(additionalDocuments); 1490 documents.add(document); 1491 1492 UpdateResponse solrResponse = solrClient.add(_solrClientProvider.getCollectionName(workspaceName), documents); 1493 int status = solrResponse.getStatus(); 1494 1495 long time_3 = System.currentTimeMillis(); 1496 getLogger().debug("Update document for content {} for workspace {} in {} ms", content.getId(), workspaceName, time_3 - time_2); 1497 1498 if (status != 0) 1499 { 1500 throw new IOException("Content indexation: got status code '" + status + "'."); 1501 } 1502 } 1503 1504 /** 1505 * Indexes read-ACl initial values for object 1506 * @param ametysObject The object 1507 * @param document The Solr document 1508 */ 1509 public void indexAclInitValues(AmetysObject ametysObject, SolrInputDocument document) 1510 { 1511 // Indexation of AmetysObject property 1512 document.addField(SolrFieldNames.IS_AMETYS_OBJECT, true); 1513 1514 // Indexation of initValues for AllowedUsers 1515 AllowedUsers allowedUsers = _readAccessHelper.allowedUsers(ametysObject); 1516 document.addField(SolrFieldNames.ACL_INIT_VALUE_ANONYMOUS, allowedUsers.isAnonymousAllowed()); 1517 document.addField(SolrFieldNames.ACL_INIT_VALUE_ANYCONNECTED, allowedUsers.isAnyConnectedUserAllowed()); 1518 _addField(document, SolrFieldNames.ACL_INIT_VALUE_ALLOWED_USERS, allowedUsers.getAllowedUsers(), UserIdentity::userIdentityToString); 1519 _addField(document, SolrFieldNames.ACL_INIT_VALUE_DENIED_USERS, allowedUsers.getDeniedUsers(), UserIdentity::userIdentityToString); 1520 _addField(document, SolrFieldNames.ACL_INIT_VALUE_ALLOWED_GROUPS, allowedUsers.getAllowedGroups(), GroupIdentity::groupIdentityToString); 1521 _addField(document, SolrFieldNames.ACL_INIT_VALUE_DENIED_GROUPS, allowedUsers.getDeniedGroups(), GroupIdentity::groupIdentityToString); 1522 } 1523 1524 private <T> void _addField(SolrInputDocument document, String fieldName, Set<T> values, Function<T, String> stringifier) 1525 { 1526 if (values == null) 1527 { 1528 return; 1529 } 1530 1531 for (T value : values) 1532 { 1533 document.addField(fieldName, stringifier.apply(value)); 1534 } 1535 } 1536 1537 /** 1538 * Index the whole workflow of a content. 1539 * @param content The content. 1540 * @param workspaceName The workspace name 1541 * @param solrClient The solr client to use 1542 * @throws Exception if an error occurs while indexing. 1543 */ 1544 protected void doIndexContentWorkflow(Content content, String workspaceName, SolrClient solrClient) throws Exception 1545 { 1546 if (content instanceof WorkflowAwareContent) 1547 { 1548 _solrWorkflowIndexer.indexAmetysObjectWorkflow((WorkflowAwareContent) content, workspaceName, solrClient); 1549 } 1550 } 1551 1552 /** 1553 * Index content attachments as new entries in the idnex 1554 * @param collection the collection of attachments 1555 * @param content the content whose attachments will be indexed 1556 * @throws Exception if something goes wrong when indexing the attachments of the content 1557 */ 1558 public void indexContentAttachments(ResourceCollection collection, Content content) throws Exception 1559 { 1560 Request request = ContextHelper.getRequest(_context); 1561 String workspaceName = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1562 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1563 indexContentAttachments(collection, content, solrClient); 1564 } 1565 1566 /** 1567 * Index content attachments as new entries in the idnex 1568 * @param collection the collection of attachments 1569 * @param content the content whose attachments will be indexed 1570 * @param solrClient The solr client to use 1571 * @throws Exception if something goes wrong when indexing the attachments of the content 1572 */ 1573 public void indexContentAttachments(ResourceCollection collection, Content content, SolrClient solrClient) throws Exception 1574 { 1575 if (collection == null) 1576 { 1577 return; 1578 } 1579 1580 try (AmetysObjectIterable<AmetysObject> children = collection.getChildren()) 1581 { 1582 for (AmetysObject object : children) 1583 { 1584 if (object instanceof ResourceCollection) 1585 { 1586 indexContentAttachments((ResourceCollection) object, content, solrClient); 1587 } 1588 else if (object instanceof Resource) 1589 { 1590 Resource resource = (Resource) object; 1591 indexContentAttachment(resource, content, solrClient); 1592 } 1593 } 1594 } 1595 } 1596 1597 /** 1598 * Index a content attachment 1599 * @param resource the content attachment as a {@link Resource} 1600 * @param content the content whose attachment is going to be indexed 1601 * @throws Exception if something goes wrong when processing the indexation of the content attachment 1602 */ 1603 public void indexContentAttachment(Resource resource, Content content) throws Exception 1604 { 1605 Request request = ContextHelper.getRequest(_context); 1606 String workspaceName = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1607 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1608 indexContentAttachment(resource, content, solrClient); 1609 } 1610 1611 /** 1612 * Index a content attachment 1613 * @param resource the content attachment as a {@link Resource} 1614 * @param content the content whose attachment is going to be indexed 1615 * @param solrClient The solr client to use 1616 * @throws Exception if something goes wrong when processing the indexation of the content attachment 1617 */ 1618 public void indexContentAttachment(Resource resource, Content content, SolrClient solrClient) throws Exception 1619 { 1620 SolrInputDocument document = new SolrInputDocument(); 1621 1622 // Prepare resource doc 1623 _indexContentAttachment(resource, document, content); 1624 1625 // Indexation of the document 1626 _indexResourceDocument(resource, document, solrClient); 1627 } 1628 1629 private void _indexContentAttachment(Resource resource, SolrInputDocument document, Content content) throws Exception 1630 { 1631 String language = content.getLanguage(); 1632 1633 _solrResourceIndexer.indexResource(resource, document, SolrFieldNames.TYPE_CONTENT_ATTACHMENT_RESOURCE, language); 1634 1635 // Need the id of the content for unindexing attachment during the unindexing of the content 1636 document.addField(SolrFieldNames.ATTACHMENT_CONTENT_ID, content.getId()); 1637 } 1638 1639 /** 1640 * Index a populated solr input document of type Resource. 1641 * @param resource the resource from which the input document is created 1642 * @param document the input document 1643 * @param solrClient The solr client to use 1644 * @throws SolrServerException if there is an error on the server 1645 * @throws IOException if there is a communication error with the server 1646 */ 1647 protected void _indexResourceDocument(Resource resource, SolrInputDocument document, SolrClient solrClient) throws SolrServerException, IOException 1648 { 1649 String resourceId = resource.getId(); 1650 1651 // Retrieve appropriate collection name 1652 Request request = ContextHelper.getRequest(_context); 1653 String workspaceName = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1654 String collectionName = _solrClientProvider.getCollectionName(workspaceName); 1655 1656 // Add document 1657 UpdateResponse solrResponse = solrClient.add(collectionName, document); 1658 int status = solrResponse.getStatus(); 1659 1660 if (status != 0) 1661 { 1662 throw new IOException("Ametys resource indexing - Expecting status code of '0' in the Solr response but got : '" + status + "'. Resource id : " + resourceId); 1663 } 1664 1665 getLogger().debug("Successful resource indexing. Resource identifier : {}", resourceId); 1666 } 1667 1668 /** 1669 * Delete repeater documents of a specified content. 1670 * @param workspaceName The workspace name 1671 * @param contentId the content ID. 1672 * @param solrClient The solr client to use 1673 * @throws Exception if an error occurs while indexing. 1674 */ 1675 protected void deleteRepeaterDocs(String contentId, String workspaceName, SolrClient solrClient) throws Exception 1676 { 1677 long time_0 = System.currentTimeMillis(); 1678 1679 // _documentType:repeater AND id:content\://xxx/* 1680 StringBuilder query = new StringBuilder(); 1681 query.append(SolrFieldNames.DOCUMENT_TYPE).append(':').append(SolrFieldNames.TYPE_REPEATER) 1682 .append(" AND id:").append(ClientUtils.escapeQueryChars(contentId)).append("/*"); 1683 1684 solrClient.deleteByQuery(_solrClientProvider.getCollectionName(workspaceName), query.toString()); 1685 1686 getLogger().debug("Successfully delete repeaters documents for content {} in {} ms", contentId, System.currentTimeMillis() - time_0); 1687 } 1688 1689 /** 1690 * Index all the resources in a given workspace. 1691 * @param workspaceName The workspace where to index 1692 * @param solrClient The solr client to use 1693 * @param progressionTracker The progression of the indexation 1694 * @return The indexation result as a Map. 1695 * @throws Exception if an error occurs while indexing. 1696 */ 1697 public Map<String, Object> indexAllResources(String workspaceName, SolrClient solrClient, SimpleProgressionTracker progressionTracker) throws Exception 1698 { 1699 Request request = ContextHelper.getRequest(_context); 1700 1701 // Retrieve the current workspace. 1702 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1703 1704 try 1705 { 1706 // Force the workspace. 1707 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1708 1709 getLogger().info("Starting the indexation of all resources for workspace {}", workspaceName); 1710 1711 long start = System.currentTimeMillis(); 1712 1713 // Delete all resources 1714 _unindexAllResources(workspaceName, solrClient); 1715 1716 Map<String, Object> results = new HashMap<>(); 1717 1718 try 1719 { 1720 TraversableAmetysObject resourceRoot = _resolver.resolveByPath(RepositoryConstants.NAMESPACE_PREFIX + ":resources"); 1721 AmetysObjectIterable<Resource> resources = resourceRoot.getChildren(); 1722 1723 IndexationResult result = doIndexResources(resources, SolrFieldNames.TYPE_RESOURCE, resourceRoot, workspaceName, solrClient, progressionTracker); 1724 1725 long end = System.currentTimeMillis(); 1726 1727 if (!result.hasErrors()) 1728 { 1729 getLogger().info("{} resources indexed without error in {} milliseconds.", result.successCount(), end - start); 1730 } 1731 else 1732 { 1733 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()); 1734 } 1735 1736 results.put("successCount", result.successCount()); 1737 if (result.hasErrors()) 1738 { 1739 results.put("errorCount", result.errorCount()); 1740 } 1741 } 1742 catch (UnknownAmetysObjectException e) 1743 { 1744 getLogger().info("There is no root for resources in current workspace."); 1745 progressionTracker.setSize(0); 1746 } 1747 1748 return results; 1749 } 1750 catch (Exception e) 1751 { 1752 String error = String.format("Failed to index all resources in workspace %s", workspaceName); 1753 getLogger().error(error, e); 1754 throw new IndexingException(error, e); 1755 } 1756 finally 1757 { 1758 // Restore context 1759 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1760 } 1761 } 1762 1763 /** 1764 * Send a collection of contents for indexation in the solr server. 1765 * @param resources the collection of contents to index. 1766 * @param documentType The document type of the resource 1767 * @param workspaceName The workspace where to index 1768 * @return the indexation result. 1769 * @throws Exception if an error occurs while indexing. 1770 */ 1771 public IndexationResult indexResources(Iterable<AmetysObject> resources, String documentType, String workspaceName) throws Exception 1772 { 1773 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1774 return indexResources(resources, documentType, null, workspaceName, solrClient); 1775 } 1776 1777 /** 1778 * Send a collection of contents for indexation in the solr server. 1779 * @param resources the collection of contents to index. 1780 * @param documentType The document type of the resource 1781 * @param resourceRoot The resource root, can be null. In that case, it will be computed for each resource. 1782 * @param workspaceName The workspace where to index 1783 * @param solrClient The solr client to use 1784 * @return the indexation result. 1785 * @throws Exception if an error occurs while indexing. 1786 */ 1787 public IndexationResult indexResources(Iterable<? extends AmetysObject> resources, String documentType, TraversableAmetysObject resourceRoot, String workspaceName, SolrClient solrClient) throws Exception 1788 { 1789 return indexResources(resources, documentType, resourceRoot, workspaceName, solrClient, ProgressionTrackerFactory.createSimpleProgressionTracker("Index resources", getLogger())); 1790 } 1791 1792 /** 1793 * Send a collection of contents for indexation in the solr server. 1794 * @param resources the collection of contents to index. 1795 * @param documentType The document type of the resource 1796 * @param resourceRoot The resource root, can be null. In that case, it will be computed for each resource. 1797 * @param workspaceName The workspace where to index 1798 * @param solrClient The solr client to use 1799 * @param progressionTracker The progression of the indexation 1800 * @return the indexation result. 1801 * @throws Exception if an error occurs while indexing. 1802 */ 1803 public IndexationResult indexResources(Iterable<? extends AmetysObject> resources, String documentType, TraversableAmetysObject resourceRoot, String workspaceName, SolrClient solrClient, SimpleProgressionTracker progressionTracker) throws Exception 1804 { 1805 Request request = ContextHelper.getRequest(_context); 1806 1807 // Retrieve the current workspace. 1808 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1809 1810 try 1811 { 1812 // Force the workspace. 1813 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1814 1815 getLogger().info("Starting indexation of several resources for workspace {}", workspaceName); 1816 1817 long start = System.currentTimeMillis(); 1818 1819 IndexationResult result = doIndexResources(resources, documentType, resourceRoot, workspaceName, solrClient, progressionTracker); 1820 1821 long end = System.currentTimeMillis(); 1822 1823 if (!result.hasErrors()) 1824 { 1825 getLogger().info("{} resources indexed without error in {} milliseconds.", result.successCount(), end - start); 1826 } 1827 else 1828 { 1829 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()); 1830 } 1831 1832 return result; 1833 } 1834 catch (Exception e) 1835 { 1836 String error = String.format("Failed to index several resources in workspace %s", workspaceName); 1837 getLogger().error(error, e); 1838 throw new IndexingException(error, e); 1839 } 1840 finally 1841 { 1842 // Restore context 1843 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1844 } 1845 } 1846 1847 /** 1848 * Send some resources for indexation in the solr server. 1849 * @param resources the resources to index. 1850 * @param documentType The document type of the resource 1851 * @param resourceRoot The resource root, can be null. In that case, it will be computed for each resource. 1852 * @param workspaceName The workspace where to index 1853 * @param solrClient The solr client to use 1854 * @param progressionTracker The progression of the indexation 1855 * @return the indexation result. 1856 * @throws Exception if an error occurs committing the results. 1857 */ 1858 protected IndexationResult doIndexResources(Iterable<? extends AmetysObject> resources, String documentType, TraversableAmetysObject resourceRoot, String workspaceName, SolrClient solrClient, SimpleProgressionTracker progressionTracker) throws Exception 1859 { 1860 try 1861 { 1862 // Add callable for each content to index 1863 List<Future<Void>> tasks = new ArrayList<>(); 1864 for (AmetysObject resource : resources) 1865 { 1866 tasks.addAll(_asyncIndexExplorerItem(resource, documentType, resourceRoot, solrClient)); 1867 } 1868 1869 // Now that everything is submitted, we can iterate and wait for result 1870 return IndexationResult.fromTasks(tasks, getLogger()); 1871 } 1872 finally 1873 { 1874 progressionTracker.increment(); 1875 } 1876 } 1877 1878 /** 1879 * Add or update a resource into Solr index 1880 * @param resource The resource to index 1881 * @param documentType The document type of the resource 1882 * @param workspaceName The workspace where to index 1883 * @throws Exception if an error occurs while indexing. 1884 */ 1885 public void indexResource(Resource resource, String documentType, String workspaceName) throws Exception 1886 { 1887 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1888 doIndexResources(Collections.singleton(resource), documentType, null, workspaceName, solrClient, ProgressionTrackerFactory.createSimpleProgressionTracker("Index resource", getLogger())); 1889 } 1890 1891 private void _unindexAllResources(String workspaceName, SolrClient solrClient) throws Exception 1892 { 1893 getLogger().debug("Unindexing all resources from Solr."); 1894 1895 String collection = _solrClientProvider.getCollectionName(workspaceName); 1896 solrClient.deleteByQuery(collection, SolrFieldNames.DOCUMENT_TYPE + ':' + SolrFieldNames.TYPE_RESOURCE); 1897 1898 getLogger().debug("Succesfully deleted all resource documents from Solr."); 1899 } 1900 1901 /** 1902 * Delete all resource documents at a given path for all workspaces and commit 1903 * @param rootId The resource root ID, must not be null. 1904 * @param path The resource path relative to the given root, must start with a slash. 1905 * @throws Exception If an error occurs while unindexing. 1906 */ 1907 public void unindexResourcesByPath(String rootId, String path) throws Exception 1908 { 1909 String[] workspaceNames = _repository.getWorkspaces(); 1910 for (String workspaceName : workspaceNames) 1911 { 1912 unindexResourcesByPath(rootId, path, workspaceName); 1913 } 1914 } 1915 1916 /** 1917 * Delete all resource documents at a given path. 1918 * @param rootId The resource root ID, must not be null. 1919 * @param path The resource path relative to the given root, must start with a slash. 1920 * @param workspaceName The workspace where to work in 1921 * @throws Exception If an error occurs while unindexing. 1922 */ 1923 public void unindexResourcesByPath(String rootId, String path, String workspaceName) throws Exception 1924 { 1925 Request request = ContextHelper.getRequest(_context); 1926 1927 // Retrieve the current workspace. 1928 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1929 1930 try 1931 { 1932 // Force the workspace. 1933 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1934 1935 getLogger().debug("Unindexing all resources at path {} in root {}", path, rootId); 1936 1937 Query query = new ResourceLocationQuery(rootId, path); 1938 1939 String collection = _solrClientProvider.getCollectionName(workspaceName); 1940 _getAutoCommitSolrClient(workspaceName).deleteByQuery(collection, query.build()); 1941 1942 getLogger().debug("Succesfully deleted resource document from Solr."); 1943 } 1944 catch (Exception e) 1945 { 1946 String error = String.format("Failed to unindex resource %s in workspace %s", path, workspaceName); 1947 getLogger().error(error, e); 1948 throw new IndexingException(error, e); 1949 } 1950 finally 1951 { 1952 // Restore workspace 1953 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 1954 } 1955 } 1956 1957 /** 1958 * Remove a resource from Solr index for all workspaces and commit 1959 * @param resourceId The id of resource to unindex 1960 * @throws Exception if an error occurs while indexing. 1961 */ 1962 public void unindexResource(String resourceId) throws Exception 1963 { 1964 String[] workspaceNames = _repository.getWorkspaces(); 1965 for (String workspaceName : workspaceNames) 1966 { 1967 unindexResource(resourceId, workspaceName); 1968 } 1969 } 1970 1971 /** 1972 * Remove a resource from the Solr index. 1973 * @param resourceId The id of resource to unindex 1974 * @param workspaceName The workspace where to work in 1975 * @throws Exception if an error occurs while unindexing. 1976 */ 1977 public void unindexResource(String resourceId, String workspaceName) throws Exception 1978 { 1979 Request request = ContextHelper.getRequest(_context); 1980 1981 // Retrieve the current workspace. 1982 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 1983 1984 try 1985 { 1986 // Force the workspace. 1987 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 1988 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 1989 1990 getLogger().debug("Unindexing resource {} from Solr.", resourceId); 1991 1992 doUnindexDocument(resourceId, workspaceName, solrClient); 1993 1994 getLogger().debug("Succesfully deleted resource {} from Solr.", resourceId); 1995 } 1996 catch (Exception e) 1997 { 1998 String error = String.format("Failed to unindex resource %s in workspace %s", resourceId, workspaceName); 1999 getLogger().error(error, e); 2000 throw new IndexingException(error, e); 2001 } 2002 finally 2003 { 2004 // Restore workspace 2005 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 2006 } 2007 } 2008 2009 private List<Future<Void>> _asyncIndexExplorerItem(AmetysObject node, String documentType, TraversableAmetysObject resourceRoot, SolrClient solrClient) throws Exception 2010 { 2011 List<Future<Void>> tasks = new ArrayList<>(); 2012 2013 if (node instanceof ResourceCollection resourceCollection) 2014 { 2015 try (AmetysObjectIterable<AmetysObject> children = resourceCollection.getChildren()) 2016 { 2017 for (AmetysObject child : children) 2018 { 2019 tasks.addAll(_asyncIndexExplorerItem(child, documentType, resourceRoot, solrClient)); 2020 } 2021 } 2022 } 2023 else if (node instanceof Resource resource) 2024 { 2025 tasks.add(_threadIndexerHelper.submitCallable(new ResourceIndexerCallable(resource, _workspaceSelector.getWorkspace(), documentType, resourceRoot, solrClient))); 2026 } 2027 2028 return tasks; 2029 } 2030 2031 /** 2032 * Process a Solr commit operation in all workspaces. 2033 * <br>Use this only after a long operation with updates sent via {@link NoAutoCommitUpdateClient} 2034 * @throws SolrServerException if there is an error on the server 2035 * @throws IOException if there is a communication error with the server 2036 */ 2037 public void commit() throws SolrServerException, IOException 2038 { 2039 String[] workspaceNames; 2040 try 2041 { 2042 workspaceNames = _repository.getWorkspaces(); 2043 for (String workspaceName : workspaceNames) 2044 { 2045 SolrClient solrClient = _getNoAutoCommitSolrClient(workspaceName); 2046 commit(workspaceName, solrClient); 2047 } 2048 } 2049 catch (RepositoryException e) 2050 { 2051 throw new RuntimeException("An exception occured while retrieving JCR workspaces. Cannot commited to Solr.", e); 2052 } 2053 } 2054 2055 /** 2056 * Process a Solr commit operation in given workspace. 2057 * @param workspaceName The workspace's name 2058 * @param solrClient The solr client to use 2059 * @throws SolrServerException if there is an error on the server 2060 * @throws IOException if there is a communication error with the server 2061 */ 2062 public void commit(String workspaceName, SolrClient solrClient) throws SolrServerException, IOException 2063 { 2064 long time_0 = System.currentTimeMillis(); 2065 2066 // Commit 2067 UpdateResponse solrResponse = solrClient.commit(_solrClientProvider.getCollectionName(workspaceName)); 2068 int status = solrResponse.getStatus(); 2069 2070 if (status != 0) 2071 { 2072 throw new IOException("Ametys indexing: Solr commit operation - Expecting status code of '0' in the Solr response but got : '" + status + "'."); 2073 } 2074 2075 getLogger().debug("Successful Solr commit operation during an Ametys indexing process in {} ms", System.currentTimeMillis() - time_0); 2076 } 2077 2078 /** 2079 * Launch a solr index optimization. 2080 * @param workspaceName The workspace's name 2081 * @param solrClient The solr client to use 2082 * @throws SolrServerException if there is an error on the server 2083 * @throws IOException if there is a communication error with the server 2084 */ 2085 public void optimize(String workspaceName, SolrClient solrClient) throws SolrServerException, IOException 2086 { 2087 UpdateResponse solrResponse = solrClient.optimize(_solrClientProvider.getCollectionName(workspaceName)); 2088 int status = solrResponse.getStatus(); 2089 2090 if (status != 0) 2091 { 2092 throw new IOException("Ametys indexing: Solr optimize operation - Expecting status code of '0' in the Solr response but got : '" + status + "'."); 2093 } 2094 2095 getLogger().debug("Successful Solr optimize operation during an Ametys indexing process."); 2096 } 2097 2098 /** 2099 * Delete all documents from the solr index. 2100 * @param workspaceName The workspace name 2101 * @param solrClient The solr client to use 2102 * @throws Exception if an error occurs while unindexing. 2103 */ 2104 public void unindexAllDocuments(String workspaceName, SolrClient solrClient) throws Exception 2105 { 2106 getLogger().debug("Deleting all documents from Solr."); 2107 2108 String collection = _solrClientProvider.getCollectionName(workspaceName); 2109 solrClient.deleteByQuery(collection, "*:*"); 2110 2111 getLogger().debug("Successfully deleted all documents from Solr."); 2112 } 2113 2114 /** 2115 * Delete a document from the Solr server. 2116 * @param id The id of the document to delete from Solr 2117 * @param workspaceName The workspace name 2118 * @param solrClient The solr client to use 2119 * @throws Exception if an error occurs while indexing. 2120 */ 2121 protected void doUnindexDocument(String id, String workspaceName, SolrClient solrClient) throws Exception 2122 { 2123 UpdateResponse solrResponse = solrClient.deleteById(_solrClientProvider.getCollectionName(workspaceName), id); 2124 int status = solrResponse.getStatus(); 2125 2126 if (status != 0) 2127 { 2128 throw new IOException("Deletion of document " + id + ": got status code '" + status + "'."); 2129 } 2130 } 2131 2132 /** 2133 * Delete content attachments documents of a given content from the Solr server. 2134 * @param contentId The id of the content 2135 * @param workspaceName The workspace name 2136 * @param solrClient The solr client to use 2137 * @throws Exception if an error occurs while indexing. 2138 */ 2139 protected void doUnindexContentAttachments(String contentId, String workspaceName, SolrClient solrClient) throws Exception 2140 { 2141 String collectionName = _solrClientProvider.getCollectionName(workspaceName); 2142 2143 Query query = new ContentAttachmentQuery(contentId); 2144 UpdateResponse solrResponse = solrClient.deleteByQuery(collectionName, query.build()); 2145 int status = solrResponse.getStatus(); 2146 2147 if (status != 0) 2148 { 2149 throw new IOException("Deletion of content attachments of content " + contentId + ": got status code '" + status + "'."); 2150 } 2151 } 2152 2153 /** 2154 * Add or update a trash element into Solr index on all workspaces and commit 2155 * @param trashElementId The id of the trash element to index 2156 * @throws Exception if an error occurs while indexing. 2157 */ 2158 public void indexTrashElement(String trashElementId) throws Exception 2159 { 2160 String[] workspaceNames = _repository.getWorkspaces(); 2161 for (String workspaceName : workspaceNames) 2162 { 2163 indexTrashElement(trashElementId, workspaceName); 2164 } 2165 } 2166 2167 /** 2168 * Add or update a trash element into Solr index 2169 * @param trashElementId The id of the trash element to index 2170 * @param workspaceName the workspace where to index 2171 * @throws Exception if an error occurs while indexing. 2172 */ 2173 public void indexTrashElement(String trashElementId, String workspaceName) throws Exception 2174 { 2175 Request request = ContextHelper.getRequest(_context); 2176 2177 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 2178 2179 // Retrieve the current workspace. 2180 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 2181 2182 try 2183 { 2184 // Force the workspace. 2185 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 2186 2187 if (_resolver.hasAmetysObjectForId(trashElementId)) 2188 { 2189 DefaultTrashElement trashElement = _resolver.resolveById(trashElementId); 2190 doIndexTrashElements(Collections.singleton(trashElement), workspaceName, solrClient, ProgressionTrackerFactory.createSimpleProgressionTracker("Index trash element", getLogger())); 2191 } 2192 } 2193 finally 2194 { 2195 // Restore context 2196 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 2197 } 2198 } 2199 2200 /** 2201 * Index all the trash elements in a given workspace. 2202 * @param workspaceName the workspace where to index 2203 * @param solrClient The solr client to use 2204 * @param progressionTracker The progression of the indexation 2205 * @return The indexation result as a Map. 2206 * @throws Exception if an error occurs while indexing. 2207 */ 2208 public Map<String, Object> indexAllTrashElements(String workspaceName, SolrClient solrClient, SimpleProgressionTracker progressionTracker) throws Exception 2209 { 2210 Request request = ContextHelper.getRequest(_context); 2211 2212 // Retrieve the current workspace. 2213 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 2214 2215 try 2216 { 2217 // Force the workspace. 2218 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 2219 2220 getLogger().info("Starting the indexation of all trash elements for workspace {}", workspaceName); 2221 2222 long start = System.currentTimeMillis(); 2223 2224 // Delete all trash elements 2225 unindexAllTrashElements(workspaceName, solrClient); 2226 2227 String query = QueryHelper.getXPathQuery(null, TrashElementFactory.TRASH_ELEMENT_NODETYPE, null); 2228 AmetysObjectIterable<DefaultTrashElement> trashElements = _resolver.query(query); 2229 2230 IndexationResult result = doIndexTrashElements(trashElements, workspaceName, solrClient, progressionTracker); 2231 2232 long end = System.currentTimeMillis(); 2233 2234 if (!result.hasErrors()) 2235 { 2236 getLogger().info("{} trash elements indexed without error in {} milliseconds.", result.successCount(), end - start); 2237 } 2238 else 2239 { 2240 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()); 2241 } 2242 2243 Map<String, Object> results = new HashMap<>(); 2244 results.put("successCount", result.successCount()); 2245 if (result.hasErrors()) 2246 { 2247 results.put("errorCount", result.errorCount()); 2248 } 2249 2250 return results; 2251 } 2252 catch (Exception e) 2253 { 2254 String error = String.format("Failed to index all trash elements in workspace %s", workspaceName); 2255 getLogger().error(error, e); 2256 throw new IndexingException(error, e); 2257 } 2258 finally 2259 { 2260 // Restore context 2261 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 2262 } 2263 } 2264 2265 /** 2266 * Remove a trash element from Solr index for all workspaces and commit 2267 * @param trashElementId The id of trash element to unindex 2268 * @throws Exception if an error occurs while indexing. 2269 */ 2270 public void unindexTrashElement(String trashElementId) throws Exception 2271 { 2272 String[] workspaceNames = _repository.getWorkspaces(); 2273 for (String workspaceName : workspaceNames) 2274 { 2275 unindexTrashElement(trashElementId, workspaceName); 2276 } 2277 } 2278 2279 /** 2280 * Remove a trash element from Solr index 2281 * @param trashElementId The id of trash element to unindex 2282 * @param workspaceName The workspace where to work in 2283 * @throws Exception if an error occurs while indexing. 2284 */ 2285 public void unindexTrashElement(String trashElementId, String workspaceName) throws Exception 2286 { 2287 Request request = ContextHelper.getRequest(_context); 2288 2289 SolrClient solrClient = _getAutoCommitSolrClient(workspaceName); 2290 2291 // Retrieve the current workspace. 2292 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 2293 2294 try 2295 { 2296 // Force the workspace. 2297 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 2298 2299 UpdateResponse solrResponse = solrClient.deleteById(_solrClientProvider.getCollectionName(workspaceName), trashElementId); 2300 int status = solrResponse.getStatus(); 2301 2302 if (status != 0) 2303 { 2304 throw new IOException("Deletion of document " + trashElementId + ": got status code '" + status + "'."); 2305 } 2306 } 2307 finally 2308 { 2309 // Restore context 2310 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 2311 } 2312 } 2313 2314 /** 2315 * Unindex all trash elements documents. 2316 * @param workspaceName The workspace name 2317 * @param solrClient The solr client to use 2318 * @throws Exception if an error occurs while unindexing. 2319 */ 2320 protected void unindexAllTrashElements(String workspaceName, SolrClient solrClient) throws Exception 2321 { 2322 getLogger().debug("Unindexing all trash elements from Solr."); 2323 2324 String collection = _solrClientProvider.getCollectionName(workspaceName); 2325 Query query = new DocumentTypeQuery(SolrFieldNames.TYPE_TRASH_ELEMENT); 2326 solrClient.deleteByQuery(collection, query.build()); 2327 2328 getLogger().debug("Succesfully deleted all trash elements documents from Solr."); 2329 } 2330 2331 /** 2332 * Send some trash elements for indexation in the solr server. 2333 * @param trashElements the trash elements to index. 2334 * @param workspaceName The workspace name 2335 * @param solrClient The solr client to use 2336 * @param progressionTracker The progression of the indexation 2337 * @return the indexation result. 2338 * @throws Exception if an error occurs committing the results. 2339 */ 2340 protected IndexationResult doIndexTrashElements(Iterable<DefaultTrashElement> trashElements, String workspaceName, SolrClient solrClient, SimpleProgressionTracker progressionTracker) throws Exception 2341 { 2342 int numberOfTrashElements = IterableUtils.size(trashElements); 2343 progressionTracker.setSize(numberOfTrashElements); 2344 2345 // Add callable for each content to index 2346 List<Future<Void>> tasks = new ArrayList<>(); 2347 for (DefaultTrashElement trashElement : trashElements) 2348 { 2349 tasks.add(_threadIndexerHelper.submitCallable(new TrashElementIndexerCallable(trashElement, workspaceName, solrClient, progressionTracker))); 2350 } 2351 2352 // Now that everything is submitted, we can iterate and wait for result 2353 return IndexationResult.fromTasks(tasks, getLogger()); 2354 } 2355 2356// /** 2357// * Get the collection to use. 2358// * @return The name of the collection to index into. 2359// */ 2360// protected String getCollection() 2361// { 2362// return _solrCorePrefix + _workspaceSelector.getWorkspace(); 2363// } 2364 2365 private static final class SchemaRequestComparator implements Comparator<SchemaRequest.Update> 2366 { 2367 public int compare(SchemaRequest.Update u1, SchemaRequest.Update u2) 2368 { 2369 return Integer.compare(_getOrder(u1), _getOrder(u2)); 2370 } 2371 2372 private int _getOrder(SchemaRequest.Update update) 2373 { 2374 // Field types are needed first to define fields 2375 if (update instanceof SchemaRequest.AddFieldType) 2376 { 2377 return 1; 2378 } 2379 2380 // Fields or dynamic fields can be defined at the same time 2381 if (update instanceof SchemaRequest.AddField || update instanceof SchemaRequest.AddDynamicField) 2382 { 2383 return 2; 2384 } 2385 2386 // Copy fields can be based on fields or dynamic fields 2387 if (update instanceof SchemaRequest.AddCopyField) 2388 { 2389 return 3; 2390 } 2391 2392 return 0; 2393 } 2394 } 2395 2396 private class ContentIndexerCallable extends AbstractIndexerCallable<Content> 2397 { 2398 private boolean _indexAttachments; 2399 private SimpleProgressionTracker _tracker; 2400 2401 @SuppressWarnings("synthetic-access") 2402 public ContentIndexerCallable(Content content, String workspaceName, boolean indexAttachments, SolrClient solrClient, SimpleProgressionTracker progressionTracker) 2403 { 2404 super(content, workspaceName, solrClient, _manager, _cocoonContext, _resolver, getLogger()); 2405 this._indexAttachments = indexAttachments; 2406 this._tracker = progressionTracker; 2407 } 2408 2409 @Override 2410 protected void process(Content content) throws Exception 2411 { 2412 try 2413 { 2414 doIndexContent(content, _workspaceName, _solrClient); 2415 doIndexContentWorkflow(content, _workspaceName, _solrClient); 2416 if (_indexAttachments) 2417 { 2418 indexContentAttachments(content.getRootAttachments(), content, _solrClient); 2419 } 2420 } 2421 finally 2422 { 2423 _tracker.increment(); 2424 } 2425 } 2426 2427 @Override 2428 protected String getObjectLabel() 2429 { 2430 return "content"; 2431 } 2432 } 2433 2434 private class ResourceIndexerCallable extends AbstractIndexerCallable<Resource> 2435 { 2436 private String _documentType; 2437 private TraversableAmetysObject _resourceRoot; 2438 2439 @SuppressWarnings("synthetic-access") 2440 public ResourceIndexerCallable(Resource resource, String workspaceName, String documentType, TraversableAmetysObject resourceRoot, SolrClient solrClient) 2441 { 2442 super(resource, workspaceName, solrClient, _manager, _cocoonContext, _resolver, getLogger()); 2443 this._documentType = documentType; 2444 this._resourceRoot = resourceRoot; 2445 } 2446 2447 @Override 2448 protected void process(Resource resource) throws Exception 2449 { 2450 SolrInputDocument document = new SolrInputDocument(); 2451 2452 _solrResourceIndexer.indexResource(resource, document, _documentType, _resourceRoot); 2453 2454 UpdateResponse solrResponse = _solrClient.add(_solrClientProvider.getCollectionName(_workspaceName), document); 2455 int status = solrResponse.getStatus(); 2456 2457 if (status != 0) 2458 { 2459 throw new IOException("Resource indexation: got status code '" + status + "'."); 2460 } 2461 } 2462 2463 @Override 2464 protected String getObjectLabel() 2465 { 2466 return "resource"; 2467 } 2468 } 2469 2470 private class TrashElementIndexerCallable extends AbstractIndexerCallable<DefaultTrashElement> 2471 { 2472 private SimpleProgressionTracker _tracker; 2473 2474 @SuppressWarnings("synthetic-access") 2475 public TrashElementIndexerCallable(DefaultTrashElement trashElement, String workspaceName, SolrClient solrClient, SimpleProgressionTracker progressionTracker) 2476 { 2477 super(trashElement, workspaceName, solrClient, _manager, _cocoonContext, _resolver, getLogger()); 2478 this._tracker = progressionTracker; 2479 } 2480 2481 @Override 2482 protected void process(DefaultTrashElement trashElement) throws Exception 2483 { 2484 try 2485 { 2486 SolrInputDocument document = new SolrInputDocument(); 2487 _solrTrashElementIndexer.indexTrashElement(trashElement, document); 2488 2489 UpdateResponse solrResponse = _solrClient.add(_solrClientProvider.getCollectionName(_workspaceName), document); 2490 int status = solrResponse.getStatus(); 2491 2492 if (status != 0) 2493 { 2494 throw new IOException("Trash element indexation: got status code '" + status + "'."); 2495 } 2496 } 2497 finally 2498 { 2499 _tracker.increment(); 2500 } 2501 } 2502 2503 @Override 2504 protected String getObjectLabel() 2505 { 2506 return "trashElement"; 2507 } 2508 } 2509}