@Override
  public void close() {
    if (stopped.compareAndSet(false, true)) { // make sure we only stop once
      try {
        worker.close();
      } catch (Exception e) {
        log.workerException(e);
      }

      this.allIndexesManager.stop();
      this.timingSource.stop();

      serviceManager.releaseAllServices();

      for (Analyzer an : this.analyzers.values()) {
        an.close();
      }
      for (AbstractDocumentBuilder documentBuilder :
          this.documentBuildersContainedEntities.values()) {
        documentBuilder.close();
      }
      for (EntityIndexBinding entityIndexBinding : this.indexBindingForEntities.values()) {
        entityIndexBinding.getDocumentBuilder().close();
      }

      // unregister statistic mbean
      if (statisticsMBeanName != null) {
        JMXRegistrar.unRegisterMBean(statisticsMBeanName);
      }
    }
  }
 @Override
 public void optimize(Class entityType) {
   EntityIndexBinding entityIndexBinding = getSafeIndexBindingForEntity(entityType);
   for (IndexManager im : entityIndexBinding.getIndexManagers()) {
     im.optimize();
   }
 }
  @Test
  public void testInitialiseDynamicShardsOnStartup() throws Exception {
    EntityIndexBinding entityIndexBinding =
        getExtendedSearchIntegrator().getIndexBindings().get(Animal.class);
    assertThat(entityIndexBinding.getIndexManagers()).hasSize(0);

    insertAnimals(elephant, spider, bear);

    assertThat(entityIndexBinding.getIndexManagers()).hasSize(2);

    assertThat(getIndexManagersAfterReopening()).hasSize(2);
  }
  @SuppressWarnings("unchecked")
  private void index(
      Object entity,
      Session session,
      InstanceInitializer sessionInitializer,
      ConversionContext conversionContext)
      throws InterruptedException {

    // abort if the thread has been interrupted while not in wait(), I/O or similar which themselves
    // would have
    // raised the InterruptedException
    if (Thread.currentThread().isInterrupted()) {
      throw new InterruptedException();
    }

    Serializable id = session.getIdentifier(entity);
    Class<?> clazz = HibernateHelper.getClass(entity);
    EntityIndexBinding entityIndexBinding = entityIndexBindings.get(clazz);
    if (entityIndexBinding == null) {
      // it might be possible to receive not-indexes subclasses of the currently indexed type;
      // being not-indexed, we skip them.
      // FIXME for improved performance: avoid loading them in an early phase.
      return;
    }

    EntityIndexingInterceptor interceptor = entityIndexBinding.getEntityIndexingInterceptor();
    if (interceptor != null) {
      IndexingOverride onAdd = interceptor.onAdd(entity);
      switch (onAdd) {
        case REMOVE:
        case SKIP:
          return;
      }
      // default: continue indexing this instance
    }

    DocumentBuilderIndexedEntity docBuilder = entityIndexBinding.getDocumentBuilder();
    TwoWayFieldBridge idBridge = docBuilder.getIdBridge();
    conversionContext.pushProperty(docBuilder.getIdKeywordName());
    String idInString = null;
    try {
      idInString =
          conversionContext.setClass(clazz).twoWayConversionContext(idBridge).objectToString(id);
    } finally {
      conversionContext.popProperty();
    }
    // depending on the complexity of the object graph going to be indexed it's possible
    // that we hit the database several times during work construction.
    AddLuceneWork addWork =
        docBuilder.createAddWork(
            tenantId, clazz, entity, id, idInString, sessionInitializer, conversionContext);
    backend.enqueueAsyncWork(addWork);
  }
 private Collection<IndexManager> uniqueIndexManagerForTypes(Collection<Class<?>> entityTypes) {
   HashMap<String, IndexManager> uniqueBackends =
       new HashMap<String, IndexManager>(entityTypes.size());
   for (Class<?> type : entityTypes) {
     EntityIndexBinding indexBindingForEntity = searchFactoryImplementor.getIndexBinding(type);
     if (indexBindingForEntity != null) {
       IndexManager[] indexManagers = indexBindingForEntity.getIndexManagers();
       for (IndexManager im : indexManagers) {
         uniqueBackends.put(im.getIndexName(), im);
       }
     }
   }
   return uniqueBackends.values();
 }
 private void sendWorkToShards(LuceneWork work, boolean forceAsync) {
   final Class<?> entityType = work.getEntityClass();
   EntityIndexBinding entityIndexBinding = searchFactoryImplementor.getIndexBinding(entityType);
   IndexShardingStrategy shardingStrategy = entityIndexBinding.getSelectionStrategy();
   if (forceAsync) {
     work.getWorkDelegate(StreamingSelectionVisitor.INSTANCE)
         .performStreamOperation(work, shardingStrategy, progressMonitor, forceAsync);
   } else {
     WorkQueuePerIndexSplitter workContext = new WorkQueuePerIndexSplitter();
     work.getWorkDelegate(TransactionalSelectionVisitor.INSTANCE)
         .performOperation(work, shardingStrategy, workContext);
     workContext.commitOperations(
         progressMonitor); // FIXME I need a "Force sync" actually for when using PurgeAll before
                           // the indexing starts
   }
 }
  @Test
  public void testDynamicCreationOfShards() throws Exception {
    EntityIndexBinding entityIndexBinding =
        getExtendedSearchIntegrator().getIndexBindings().get(Animal.class);
    assertThat(entityIndexBinding.getIndexManagers()).hasSize(0);

    insertAnimals(elephant);
    assertThat(entityIndexBinding.getIndexManagers()).hasSize(1);

    insertAnimals(spider);
    assertThat(entityIndexBinding.getIndexManagers()).hasSize(2);

    insertAnimals(bear);
    assertThat(entityIndexBinding.getIndexManagers()).hasSize(2);

    assertEquals(2, getNumberOfDocumentsInIndex("Animal.Mammal"));
    assertEquals(1, getNumberOfDocumentsInIndex("Animal.Insect"));
  }
  @Override
  public IndexReader open(Class<?>... entities) {
    if (entities.length == 0) {
      throw log.needAtLeastOneIndexedEntityType();
    }

    HashMap<String, IndexManager> indexManagers = new HashMap<String, IndexManager>();
    for (Class<?> type : entities) {
      EntityIndexBinding entityIndexBinding = searchFactory.getSafeIndexBindingForEntity(type);
      IndexManager[] indexManagersForAllShards =
          entityIndexBinding.getSelectionStrategy().getIndexManagersForAllShards();
      for (IndexManager im : indexManagersForAllShards) {
        indexManagers.put(im.getIndexName(), im);
      }
    }
    IndexManager[] uniqueIndexManagers =
        indexManagers.values().toArray(new IndexManager[indexManagers.size()]);
    return MultiReaderFactory.openReader(uniqueIndexManagers);
  }
 @Override
 public IndexedTypeDescriptor getIndexedTypeDescriptor(Class<?> entityType) {
   IndexedTypeDescriptor typeDescriptor;
   if (indexedTypeDescriptors.containsKey(entityType)) {
     typeDescriptor = indexedTypeDescriptors.get(entityType);
   } else {
     EntityIndexBinding indexBinder = indexBindingForEntities.get(entityType);
     IndexedTypeDescriptor indexedTypeDescriptor;
     if (indexBinder == null) {
       indexedTypeDescriptor = new IndexedTypeDescriptorForUnindexedType(entityType);
     } else {
       indexedTypeDescriptor =
           new IndexedTypeDescriptorImpl(
               indexBinder.getDocumentBuilder().getMetadata(), indexBinder.getIndexManagers());
     }
     indexedTypeDescriptors.put(entityType, indexedTypeDescriptor);
     typeDescriptor = indexedTypeDescriptor;
   }
   return typeDescriptor;
 }
 @Override
 public Analyzer getAnalyzer(Class<?> clazz) {
   EntityIndexBinding entityIndexBinding = getSafeIndexBindingForEntity(clazz);
   DocumentBuilderIndexedEntity builder = entityIndexBinding.getDocumentBuilder();
   return builder.getAnalyzer();
 }
 private Work interceptWork(EntityIndexBinding indexBindingForEntity, Work work) {
   if (indexBindingForEntity == null) {
     return work;
   }
   EntityIndexingInterceptor interceptor = indexBindingForEntity.getEntityIndexingInterceptor();
   if (interceptor == null) {
     return work;
   }
   IndexingOverride operation;
   switch (work.getType()) {
     case ADD:
       operation = interceptor.onAdd(work.getEntity());
       break;
     case UPDATE:
       operation = interceptor.onUpdate(work.getEntity());
       break;
     case DELETE:
       operation = interceptor.onDelete(work.getEntity());
       break;
     case COLLECTION:
       operation = interceptor.onCollectionUpdate(work.getEntity());
       break;
     case PURGE:
     case PURGE_ALL:
     case INDEX:
     case DELETE_BY_QUERY:
       operation = IndexingOverride.APPLY_DEFAULT;
       break;
     default:
       throw new AssertionFailure("Unknown work type: " + work.getType());
   }
   Work result = work;
   Class<?> entityClass = work.getEntityClass();
   switch (operation) {
     case APPLY_DEFAULT:
       break;
     case SKIP:
       result = null;
       log.forceSkipIndexOperationViaInterception(entityClass, work.getType());
       break;
     case UPDATE:
       result =
           new Work(work.getTenantIdentifier(), work.getEntity(), work.getId(), WorkType.UPDATE);
       log.forceUpdateOnIndexOperationViaInterception(entityClass, work.getType());
       break;
     case REMOVE:
       // This works because other Work constructors are never used from WorkType ADD, UPDATE,
       // REMOVE, COLLECTION
       // TODO should we force isIdentifierRollback to false if the operation is not a delete?
       result =
           new Work(
               work.getTenantIdentifier(),
               work.getEntity(),
               work.getId(),
               WorkType.DELETE,
               work.isIdentifierWasRolledBack());
       log.forceRemoveOnIndexOperationViaInterception(entityClass, work.getType());
       break;
     default:
       throw new AssertionFailure("Unknown action type: " + operation);
   }
   return result;
 }