package fi.otavanopisto.muikku.plugins.search;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.ejb.Singleton;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerConfig;
import javax.ejb.TimerService;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import org.apache.commons.lang3.math.NumberUtils;
import fi.otavanopisto.muikku.controller.PluginSettingsController;
import fi.otavanopisto.muikku.model.users.UserEntity;
import fi.otavanopisto.muikku.model.users.UserGroupEntity;
import fi.otavanopisto.muikku.model.workspace.WorkspaceEntity;
import fi.otavanopisto.muikku.schooldata.WorkspaceEntityController;
import fi.otavanopisto.muikku.schooldata.entity.UserGroup;
import fi.otavanopisto.muikku.search.SearchIndexer;
import fi.otavanopisto.muikku.search.SearchReindexEvent;
import fi.otavanopisto.muikku.search.SearchReindexEvent.Task;
import fi.otavanopisto.muikku.users.UserEntityController;
import fi.otavanopisto.muikku.users.UserGroupController;
import fi.otavanopisto.muikku.users.UserGroupEntityController;
@ApplicationScoped
@Singleton
public class SchoolDataSearchReindexListener {
@Inject
private Logger logger;
@Inject
private WorkspaceEntityController workspaceEntityController;
@Inject
private UserEntityController userEntityController;
@Inject
private UserGroupController userGroupController;
@Inject
private UserGroupEntityController userGroupEntityController;
@Inject
private PluginSettingsController pluginSettingsController;
@Inject
private SearchIndexer indexer;
@Inject
private UserIndexer userIndexer;
@Inject
private WorkspaceIndexer workspaceIndexer;
@Resource
private TimerService timerService;
private static final int DEFAULT_TIMEOUT_SECONDS = 5;
private static final int DEFAULT_BATCH_SIZE = 50;
@PostConstruct
public void init() {
active = false;
}
public void onReindexEvent(@Observes SearchReindexEvent event) {
if (active) {
logger.log(Level.INFO, "Already reindexing, refused to start another reindexing task.");
return;
}
active = true;
tasks = event.getTasks();
if (tasks.contains(Task.USERS)) {
setOffset("userIndex", 0);
}
if (tasks.contains(Task.WORKSPACES)) {
setOffset("workspaceIndex", 0);
}
if (tasks.contains(Task.USER_GROUPS)) {
setOffset("groupIndex", 0);
}
logger.log(Level.INFO, "Reindex initiated.");
startTimer(getTimeout());
}
@Timeout
private void onTimeOut(Timer timer) {
logger.log(Level.INFO, "Commencing Reindex task.");
try {
boolean allDone = true;
if (tasks.contains(Task.USERS)) {
allDone = allDone && reindexUsers();
}
if (allDone && tasks.contains(Task.WORKSPACES)) {
allDone = allDone && reindexWorkspaceEntities();
}
if (allDone && tasks.contains(Task.USER_GROUPS)) {
allDone = allDone && reindexUserGroups();
}
if (!allDone) {
startTimer(getTimeout());
} else {
logger.log(Level.INFO, "Reindexing complete.");
active = false;
}
} catch (Exception ex) {
logger.log(Level.SEVERE, "Reindexing of entities failed.", ex);
}
}
private boolean reindexWorkspaceEntities() {
try {
List<WorkspaceEntity> workspaceEntities = workspaceEntityController.listWorkspaceEntities();
int workspaceIndex = getOffset("workspaceIndex");
if (workspaceIndex < workspaceEntities.size()) {
int last = Math.min(workspaceEntities.size(), workspaceIndex + getBatchSize());
for (int i = workspaceIndex; i < last; i++) {
WorkspaceEntity workspaceEntity = workspaceEntities.get(i);
workspaceIndexer.indexWorkspace(workspaceEntity);
}
logger.log(Level.INFO, "Reindexed batch of workspaces (" + workspaceIndex + "-" + last + ")");
setOffset("workspaceIndex", workspaceIndex + getBatchSize());
return false;
} else
return true;
} catch (Exception ex) {
logger.log(Level.SEVERE, "Could not finish indexing workspace entities.", ex);
return true;
}
}
private boolean reindexUsers() {
try {
List<UserEntity> users = userEntityController.listUserEntities();
int userIndex = getOffset("userIndex");
if (userIndex < users.size()) {
int last = Math.min(users.size(), userIndex + getBatchSize());
for (int i = userIndex; i < last; i++) {
try {
UserEntity userEntity = users.get(i);
userIndexer.indexUser(userEntity);
} catch (Exception uex) {
logger.log(Level.SEVERE, "Failed indexing userentity", uex);
}
}
logger.log(Level.INFO, "Reindexed batch of users (" + userIndex + "-" + last + ")");
setOffset("userIndex", userIndex + getBatchSize());
return false;
} else
return true;
} catch (Exception ex) {
logger.log(Level.SEVERE, "Could not finish indexing user entities.", ex);
return true;
}
}
private boolean reindexUserGroups() {
try {
List<UserGroupEntity> userGroups = userGroupEntityController.listUserGroupEntities();
int groupIndex = getOffset("groupIndex");
if (groupIndex < userGroups.size()) {
int last = Math.min(userGroups.size(), groupIndex + getBatchSize());
for (int i = groupIndex; i < last; i++) {
UserGroupEntity groupEntity = userGroups.get(i);
UserGroup userGroup = userGroupController.findUserGroup(groupEntity.getSchoolDataSource(), groupEntity.getIdentifier());
try {
indexer.index(UserGroup.class.getSimpleName(), userGroup);
} catch (Exception e) {
logger.log(Level.WARNING, "could not index UserGroup #" + groupEntity.getSchoolDataSource() + '/' + groupEntity.getIdentifier(), e);
}
}
logger.log(Level.INFO, "Reindexed batch of usergroups (" + groupIndex + "-" + last + ")");
setOffset("groupIndex", groupIndex + getBatchSize());
return false;
} else
return true;
} catch (Exception ex) {
logger.log(Level.SEVERE, "Could not finish indexing usergroup entities.", ex);
return true;
}
}
private int getBatchSize() {
return NumberUtils.toInt(pluginSettingsController.getPluginSetting("school-data", "school-data.reindexer.batch"), DEFAULT_BATCH_SIZE);
}
private int getTimeout() {
return NumberUtils.toInt(pluginSettingsController.getPluginSetting("school-data", "school-data.reindexer.timeout"), DEFAULT_TIMEOUT_SECONDS) * 1000;
}
private int getOffset(String var) {
return NumberUtils.toInt(pluginSettingsController.getPluginSetting("school-data", "school-data.reindexer." + var + ".offset"));
}
private void setOffset(String var, int offset) {
pluginSettingsController.setPluginSetting("school-data", "school-data.reindexer." + var + ".offset", String.valueOf(offset));
}
private void startTimer(int duration) {
if (this.timer != null) {
try {
this.timer.cancel();
} catch (Exception e) {
}
this.timer = null;
}
TimerConfig timerConfig = new TimerConfig();
timerConfig.setPersistent(false);
this.timer = timerService.createSingleActionTimer(duration, timerConfig);
}
private Timer timer;
private boolean active;
private List<Task> tasks;
}