/*
* $Id$
*
* Copyright 2008-14 Glencoe Software, Inc. All rights reserved.
* Use is subject to license terms supplied in LICENSE.txt
*/
package ome.services.fulltext;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import ome.api.IQuery;
import ome.services.eventlogs.AllEntitiesPseudoLogLoader;
import ome.services.eventlogs.AllEventsLogLoader;
import ome.services.eventlogs.EventLogLoader;
import ome.services.eventlogs.PersistentEventLogLoader;
import ome.services.sessions.SessionManager;
import ome.services.util.Executor;
import ome.system.OmeroContext;
import ome.system.Principal;
import ome.system.ServiceFactory;
import ome.system.metrics.Metrics;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import sun.misc.Signal;
import sun.misc.SignalHandler;
/**
* Commandline entry-point for various full text actions. Commands include:
* <ul>
* <li>full - Index full database</li>
* <li>events - Index all events</li>
* </ul>
*
* @author Josh Moore, josh at glencoesoftware.com
* @since 3.0-Beta3
*/
public class Main {
private final static Logger log = LoggerFactory.getLogger(Main.class);
static AtomicBoolean shutdown = new AtomicBoolean(false);
static String uuid;
static String[] excludes;
static OmeroContext context;
static Executor executor;
static SessionFactory factory;
static IQuery rawQuery;
static SessionManager manager;
static FullTextBridge bridge;
static PersistentEventLogLoader loader;
static Metrics metrics;
// Setup
public static void init() {
if (shutdown.get()) {
return; // EARLY EXIT
}
context = OmeroContext.getInstance("ome.fulltext");
try {
// Now that we're using the fulltext context we need
// to disable the regular processing, otherwise there
// are conflicts.
Scheduler scheduler = context.getBean("scheduler", Scheduler.class);
scheduler.pauseAll();
} catch (SchedulerException se) {
throw new RuntimeException(se);
}
SignalHandler handler = new SignalHandler() {
public void handle(Signal sig) {
close(sig, null);
}
};
for (String sig : new String[]{"INT","TERM","BREAK"}) {
try {
Signal.handle(new Signal(sig), handler);
} catch (IllegalArgumentException iae) {
// Ok. BREAK will not exist on non-Windows systems, for example.
}
}
uuid = context.getBean("uuid", String.class);
executor = (Executor) context.getBean("executor");
factory = (SessionFactory) context.getBean("sessionFactory");
rawQuery = (IQuery) context.getBean("internal-ome.api.IQuery");
manager = (SessionManager) context.getBean("sessionManager");
bridge = (FullTextBridge) context.getBean("fullTextBridge");
loader = (PersistentEventLogLoader) context.getBean("eventLogLoader");
metrics = (Metrics) context.getBean("metrics");
String excludesStr = context.getProperty("omero.search.excludes");
if (excludesStr != null) {
excludes = excludesStr.split(",");
} else {
excludes = new String[]{};
}
}
public static void close(Signal sig, Integer rc) {
if (!shutdown.get()) {
if (sig != null) {
log.info(sig.getName() + ": Shutdown requested.");
}
shutdown.set(true);
OmeroContext copy = context;
context = null;
copy.close();
log.info("Done");
if (sig != null) {
System.exit(sig.getNumber());
} else {
System.exit(rc);
}
}
}
protected static FullTextThread createFullTextThread(EventLogLoader loader) {
return createFullTextThread(loader, false);
}
protected static FullTextThread createFullTextThread(EventLogLoader loader,
boolean dryRun) {
final FullTextIndexer fti = new FullTextIndexer(loader, metrics);
fti.setApplicationContext(context);
fti.setDryRun(dryRun);
final FullTextThread ftt = new FullTextThread(manager, executor, fti,
bridge);
return ftt;
}
// Public usage
public static void usage() {
StringBuilder sb = new StringBuilder();
sb.append("usage: [-Dlogback.configurationFile=stderr.xml] ");
sb.append("ome.service.fulltext.Main [help|foreground|dryrun|reset|"
+ "standalone|events|full|");
sb.append("reindex class1 class2 class3 ...]\n");
System.out.println(sb.toString());
System.exit(-2);
}
public static void main(String[] args) throws Throwable {
int rc = 0;
try {
if (args == null || args.length == 0) {
usage();
} else if ("reset".equals(args[0])) {
reset(args);
} else if ("dryrun".equals(args[0])) {
foreground(true, args);
} else if ("foreground".equals(args[0])) {
foreground(false, args);
} else if ("standalone".equals(args[0])) {
standalone(args);
} else if ("events".equals(args[0])) {
indexAllEvents();
} else if ("full".equals(args[0])) {
indexFullDb();
} else if ("reindex".equals(args[0])) {
if (args.length < 2) {
usage(); // EARLY EXIT
}
Set<String> set = new HashSet<String>();
for (int i = 1; i < args.length; i++) {
set.add(args[i]);
}
indexByClass(set);
} else {
usage();
}
} catch (Throwable t) {
rc = 1;
t.printStackTrace();
} finally {
close(null, rc);
}
}
public static void indexFullDb() {
init();
final AllEntitiesPseudoLogLoader loader = new AllEntitiesPseudoLogLoader();
loader.setQueryService(rawQuery);
loader.setExcludes(excludes);
loader.setClasses(factory.getAllClassMetadata().keySet());
final FullTextThread ftt = createFullTextThread(loader);
while (loader.more() > 0) {
ftt.run();
}
}
public static void indexByClass(Set<String> set) {
init();
final AllEntitiesPseudoLogLoader loader = new AllEntitiesPseudoLogLoader();
loader.setQueryService(rawQuery);
loader.setClasses(set);
final FullTextThread ftt = createFullTextThread(loader);
while (loader.more() > 0) {
ftt.run();
}
}
public static void indexAllEvents() {
init();
final AllEventsLogLoader loader = new AllEventsLogLoader();
loader.setExcludes(excludes);
loader.setQueryService(rawQuery);
final FullTextThread ftt = createFullTextThread(loader);
while (loader.more() > 0) {
ftt.run();
}
}
/**
* Can be used to reset the value that the {@link PersistentEventLogLoader}
* would read if started now.
*/
public static void reset(String[] args) {
init();
long oldValue = -1;
long newValue = 0;
if (args == null || args.length != 2) {
System.out.println("Using 0 as reset target");
} else {
newValue = Long.valueOf(args[1]);
}
oldValue = loader.getCurrentId();
loader.setCurrentId(newValue);
System.out.println("=================================================");
System.out.println(String.format("Value reset to %s. Was %s",
newValue, oldValue));
System.out.println("=================================================");
}
/**
* Uses a {@link PersistentEventLogLoader} and cycles through all
* the remaining logs. Reset can be called first for a complete
* re-indexing.
*/
public static void foreground(boolean dryrun, String[] args) {
init();
final FullTextThread ftt = createFullTextThread(loader, dryrun);
long loops = 0;
long current = current(loader);
while (true) {
// Quartz usually would wait 3 seconds here.
loops++;
ftt.run();
long newCurrent = current(loader);
if (newCurrent == current) {
break;
} else {
current = newCurrent;
}
}
System.out.println("=================================================");
System.out.println(String.format(
"Done in %s loops. Now at: %s", loops, current));
System.out.println("=================================================");
}
/**
* Starts up and simply waits until told by the grid to disconnect.
*/
public static void standalone(String[] args) {
Ice.Communicator ic = Ice.Util.initialize(args);
Ice.ObjectAdapter oa = ic.createObjectAdapter("IndexerAdapter");
oa.activate();
String cron = ic.getProperties().getProperty("omero.search.cron");
if (cron == null || cron.length() == 0) {
System.out.println("Using default cron value.");
} else {
System.setProperty("omero.search.cron",cron);
}
try {
init(); // Starts cron
} finally {
ic.waitForShutdown();
}
}
private static long current(final PersistentEventLogLoader loader) {
Principal p = new Principal(uuid);
return (Long) executor.execute(p, new Executor.SimpleWork(loader, "more"){
@Override
@Transactional(readOnly=false)
public Object doWork(Session session, ServiceFactory sf) {
return loader.getCurrentId();
}
});
}
}