/**
* (C) Copyright 2013 Jabylon (http://www.jabylon.org) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
/**
*
*/
package org.jabylon.index.properties.jobs.impl;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.List;
import java.util.Map;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.emf.cdo.eresource.CDOResource;
import org.eclipse.emf.cdo.net4j.CDONet4jSession;
import org.eclipse.emf.cdo.view.CDOView;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.jabylon.cdo.connector.RepositoryConnector;
import org.jabylon.cdo.server.ServerConstants;
import org.jabylon.index.properties.IndexActivator;
import org.jabylon.index.properties.impl.PropertyFileAnalyzer;
import org.jabylon.properties.Project;
import org.jabylon.properties.PropertyFile;
import org.jabylon.properties.PropertyFileDescriptor;
import org.jabylon.properties.Workspace;
import org.jabylon.properties.types.impl.TMXConverter;
import org.jabylon.scheduler.JobExecution;
import org.jabylon.scheduler.JobUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Johannes Utzig (jutzig.dev@googlemail.com)
*
*/
@Component(enabled=true,immediate=true)
@Service
public class ReorgIndexJob implements JobExecution {
public static final String JOB_ID = "job.reorg.index";
private static final Logger logger = LoggerFactory.getLogger(ReorgIndexJob.class);
@org.apache.felix.scr.annotations.Property(value="true", name=JobExecution.PROP_JOB_ACTIVE)
public static final String DEFAULT_ACTIVE = "true";
/** at 2 am every day*/
@org.apache.felix.scr.annotations.Property(value="0 0 2 * * ?",name=JobExecution.PROP_JOB_SCHEDULE)
public static final String DEFAULT_SCHEDULE = "0 0 2 * * ?";
@org.apache.felix.scr.annotations.Property(value="%reorg.job.name", name=JobExecution.PROP_JOB_NAME)
private String NAME = JobExecution.PROP_JOB_NAME;
/** at 2 am every day*/
@org.apache.felix.scr.annotations.Property(value="%reorg.job.description", name=JobExecution.PROP_JOB_DESCRIPTION)
private String DESCRIPTION = JobExecution.PROP_JOB_DESCRIPTION;
/**
*
*/
public ReorgIndexJob() {
}
@Override
public void run(IProgressMonitor monitor, Map<String, Object> jobContext) throws Exception {
RepositoryConnector connector = JobUtil.getRepositoryConnector(jobContext);
indexWorkspace(connector, monitor);
}
@Override
public boolean retryOnError() {
// retry if the index is currently locked
return true;
}
private static void indexWorkspace(Workspace workspace, IndexWriter writer, IProgressMonitor monitor) throws CorruptIndexException, IOException {
EList<Project> projects = workspace.getChildren();
SubMonitor submon = SubMonitor.convert(monitor,"Rebuilding Index", projects.size()*10);
PropertyFileAnalyzer analyzer = new PropertyFileAnalyzer();
try{
for (Project project : projects) {
SubMonitor mon = submon.newChild(10, 0);
mon.beginTask("", 100); //TODO that is not exactly accurate, but at least it moves :-)
String message = "Indexing {0}";
submon.setTaskName(MessageFormat.format(message, project.getName()));
TreeIterator<EObject> contents = project.eAllContents();
while (contents.hasNext()) {
checkCanceled(monitor);
EObject next = contents.next();
if (next instanceof PropertyFileDescriptor) {
PropertyFileDescriptor descriptor = (PropertyFileDescriptor) next;
mon.subTask(descriptor.getLocation().toString());
List<Document> documents = analyzer.createDocuments(descriptor);
for (Document document : documents) {
writer.addDocument(document);
}
mon.worked(1);
}
}
mon.done();
}
}
finally {
monitor.done();
}
}
/**
* analyzes the TMX directory
* @param writer
* @param monitor
*/
private static void indexTMX(IndexWriter writer, IProgressMonitor monitor) {
PropertyFileAnalyzer analyzer = new PropertyFileAnalyzer();
File workingDir = new File(ServerConstants.WORKING_DIR);
File tmx = new File(workingDir,"tmx");
File[] files = tmx.listFiles();
if(files==null)
return;
for (File file : files) {
TMXConverter converter = new TMXConverter();
try {
PropertyFile propertyFile = converter.load(new FileInputStream(file), "UTF-8");
List<Document> documents = analyzer.createTMXDocuments(propertyFile, URI.createFileURI("tmx/"+file.getName()));
for (Document document : documents) {
writer.addDocument(document);
}
} catch (FileNotFoundException e) {
logger.error("Could not load TMX file", e);
} catch (IOException e) {
logger.error("Could not load TMX file", e);
}
}
}
private static void checkCanceled(IProgressMonitor monitor) {
if(monitor.isCanceled())
throw new OperationCanceledException();
}
public static void indexWorkspace(RepositoryConnector connector, IProgressMonitor monitor) throws CorruptIndexException, IOException {
long time = System.currentTimeMillis();
logger.info("Reorg of search index started");
IndexWriter writer = null;
CDONet4jSession session = null;
SubMonitor submon = SubMonitor.convert(monitor, 100);
try {
writer = IndexActivator.getDefault().obtainIndexWriter();
writer.deleteAll();
session = connector.createSession();
CDOView view = connector.openView(session);
CDOResource resource = view.getResource(ServerConstants.WORKSPACE_RESOURCE);
Workspace workspace = (Workspace) resource.getContents().get(0);
indexWorkspace(workspace, writer, submon.newChild(95));
indexTMX(writer,submon.newChild(5));
writer.commit();
} catch (OutOfMemoryError error) {
logger.error("Out of memory during index reorg",error);
//As suggested by lucene documentation
writer.close();
} catch (Exception e) {
logger.error("Exception during index reorg. Rolling back",e);
if (writer != null)
writer.rollback();
throw new IllegalStateException("Failed to write index",e);
} finally {
if(monitor!=null)
monitor.done();
if(session!=null)
{
session.close();
}
IndexActivator.getDefault().returnIndexWriter(writer);
}
long duration = (System.currentTimeMillis() - time) / 1000;
logger.info("Search Index Reorg finished. Took {} seconds",duration);
}
@Override
public String getID() {
return JOB_ID;
}
}