AbstractIndexingStore.java
/*
* Copyright © 2023-2025 The CTAN Team and individual authors
*
* This file is distributed under the 3-clause BSD license.
* See file LICENSE for details.
*/
package org.ctan.site.stores.base;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import org.apache.lucene.index.CorruptIndexException;
import org.ctan.site.CtanConfiguration.CtanConfig;
import org.ctan.site.services.search.base.IndexType;
import org.ctan.site.services.search.base.IndexingSession;
import org.ctan.site.services.search.base.Searchable;
import org.hibernate.SessionFactory;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.dropwizard.util.Generics;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
/**
* The class <code>AbstractIndexingStore</code> contains the abstract repository
* for CRUD operations on an indexed entity.
*
* @param <T> the type of the underlying entity
*
* @author <a href="mailto:gene@ctan.org">Gerd Neugebauer</a>
*/
@Slf4j
public abstract class AbstractIndexingStore<T extends Searchable>
extends
AbstractStore<T>
implements
IndexingStore {
/**
* The field <code>simpleName</code> contains the name of the entity.
*/
private String simpleName;
/**
* The field <code>indexingSession</code> contains the indexing session.
*/
private IndexingSession indexingSession;
/**
* This is the constructor for <code>AbstractStore</code>.
*
* @param sessionFactory the session factory
* @param indexingSession the indexing session
*/
@SuppressWarnings("unchecked")
@SuppressFBWarnings(value = {"CT_CONSTRUCTOR_THROW", "EI_EXPOSE_REP2"})
public AbstractIndexingStore(@NonNull SessionFactory sessionFactory,
@NonNull IndexingSession indexingSession) {
super(sessionFactory);
this.indexingSession = indexingSession;
this.simpleName = ((Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0])
.getSimpleName();
}
/**
* The method <code>count</code> provides means to count all entities in the
* database.
*
* @return the total number of entities
*/
public Long count() {
Class<?> entityClass = Generics.getTypeParameter(getClass());
CriteriaBuilder qb = currentSession().getCriteriaBuilder();
CriteriaQuery<Long> cq = qb.createQuery(Long.class);
cq.select(qb.count(cq.from(entityClass)));
return currentSession().createQuery(cq).getSingleResult();
}
/**
* The method <code>findAll</code> provides means to find a list of all
* entries.
*
* @return the list of entries
*/
public List<T> findAll() {
Class<?> entityClass = Generics.getTypeParameter(getClass());
var query = criteriaQuery();
query.from(entityClass);
return list(query);
}
/**
* The method <code>indexType</code> provides means to retrieve the index
* type.
*
* @return the index type
*/
protected abstract IndexType indexType();
/**
* The method <code>remove</code> provides means to remove an entity. A new
* session is created and closed at the end.
*
* @param id the id
* @return {@code true} iff something has been removed
*/
@Override
public boolean remove(@NonNull Long id) {
T entity = get(id);
if (entity == null) {
return false;
}
return remove(entity);
}
/**
* The method <code>remove</code> provides means to remove an entity. A new
* session is created and closed at the end.
*
* @param entity the entity
* @return {@code true} iff something has been removed
*/
@Override
public boolean remove(@NonNull T entity) {
String indexPath = entity.indexPath();
for (var it : CtanConfig.LOCALES) {
try {
indexingSession.remove(indexType(), indexPath, it);
} catch (IOException e) {
log.error(e.toString());
}
}
return super.remove(entity);
}
/**
* The method <code>save</code> provides means to store an entity in the
* database.
*
* @param entity the entity
* @return the updated entity
*/
@Override
public T save(@NonNull T entity) {
try {
entity.updateIndex(indexingSession);
} catch (IOException e) {
log.error(e.toString());
}
return persist(entity);
}
/**
* The method <code>updateIndex</code> provides means to update all entries
* in the database. Note that entries which are gone are not removed!
*
* @throws IOException in case of an I/O error
* @throws CorruptIndexException in case of an error in an index
*/
@Override
public void updateIndex() throws CorruptIndexException, IOException {
for (var it : findAll()) {
it.updateIndex(indexingSession);
}
}
}