package com.ontology2.centipede.shell;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.annotations.VisibleForTesting;
import com.ontology2.centipede.errors.MisconfigurationException;
import com.ontology2.centipede.parser.OptionParser;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.google.common.collect.Lists;
import org.springframework.core.convert.ConversionService;
import static com.google.common.collect.Lists.newArrayList;
public class CentipedeShell extends CommandLineApplication {
private static Log logger = LogFactory.getLog(CentipedeShell.class);
public CentipedeShell() {
}
public List<String> getApplicationContextPath() {
return newArrayList("com/ontology2/centipede/shell/applicationContext.xml");
}
private List<String> getBootstrapApplicationContextPath() {
return newArrayList("com/ontology2/centipede/parser/bootstrapContext.xml");
}
@VisibleForTesting
static AbstractApplicationContext newContext(List<String> applicationContextPath) {
return new ClassPathXmlApplicationContext(applicationContextPath.toArray(new String[]{}));
}
@VisibleForTesting
static AbstractApplicationContext newContext(List<String> applicationContextPath,boolean beLazy) {
if(beLazy) {
applicationContextPath.add("classpath:com/ontology2/centipede/shell/addLazinessAttributeToAllBeanDefinitions.xml");
} else {
applicationContextPath.add("classpath:com/ontology2/centipede/shell/addEagernessAttributeToAllBeanDefinitions.xml");
}
return new ClassPathXmlApplicationContext(applicationContextPath.toArray(new String[]{}));
}
private AbstractApplicationContext context;
@Override
protected void _run(String[] arguments) throws Exception {
CentipedeShellOptions centipedeOptions = parseOptions(arguments);
List<String> contextPath=getApplicationContextPath();
context = createApplicationContext(centipedeOptions, contextPath);
context.registerShutdownHook();
executePositionalArguments(centipedeOptions.positional);
closeContext(context);
}
@VisibleForTesting
AbstractApplicationContext createApplicationContext(CentipedeShellOptions centipedeOptions, List<String> contextPath) {
contextPath.addAll(centipedeOptions.applicationContext);
contextPath.addAll(centipedeOptions.applicationContext);
if(centipedeOptions.eager && centipedeOptions.lazy)
throw new MisconfigurationException("Cannot force eager and lazy load at same time");
Boolean forcedMode =
centipedeOptions.lazy ? Boolean.TRUE :
(centipedeOptions.eager ? Boolean.FALSE : isLazyByDefault());
return (forcedMode==null) ? newContext(contextPath) :
newContext(contextPath,forcedMode);
}
private CentipedeShellOptions parseOptions(String[] arguments) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ApplicationContext bootstrapContext=newContext(getBootstrapApplicationContextPath());
OptionParser parser=new OptionParser(CentipedeShellOptions.class);
wireupOptionParser(bootstrapContext, parser);
closeContext(bootstrapContext);
return (CentipedeShellOptions)
parser.parse(newArrayList(arguments));
}
private void closeContext(ApplicationContext that) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
that.getClass().getMethod("close").invoke(that);
}
private void executePositionalArguments(List<String> argumentList) {
String[] arguments=argumentList.toArray(new String[0]);
if(arguments.length==0) {
usage();
}
String action=arguments[0];
if(action.equals("run")) {
runAction(arguments);
} else if(action.equals("list")) {
listAction(arguments);
} else {
List<String> amendedArguments=newArrayList();
amendedArguments.add("run*");
for(String argument:arguments) {
amendedArguments.add(argument);
}
runAction(amendedArguments.toArray(new String[0]));
}
}
private void wireupOptionParser(ApplicationContext bootstrapContext, OptionParser parser) {
parser.conversionService=(ConversionService) bootstrapContext.getBean("conversionService");
}
private void listAction(String[] arguments) {
Map<String, CommandLineApplication> all = context.getBeansOfType(CommandLineApplication.class);
for(Entry<String, CommandLineApplication> that:all.entrySet()) {
String beanName=that.getKey();
System.out.println(beanName);
}
}
public void runAction(String[] arguments) {
if (arguments.length<2) {
usage();
}
String application=arguments[1];
CommandLineApplication app=null;
try {
app = context.getBean(application,CommandLineApplication.class);
} catch(BeanNotOfRequiredTypeException ex) {
die("Application ["+application+"] not found");
} catch(NoSuchBeanDefinitionException ex) {
die("Application ["+application+"] not found");
} ;
String[] innerArguments= arguments.length<3
? new String[0]
: Arrays.copyOfRange(arguments, 2, arguments.length);
app.run(innerArguments);
}
/**
* @return the name of the shell script that wraps this application
*/
public String getShellName() {
return "centipede";
}
private void usage() {
System.out.println("usage:");
System.out.println();
System.out.println(getShellName()+" list");
System.out.println(getShellName()+" run <application> ...");
System.out.println();
System.out.println("<action> = run");
System.out.println("<application> the name of the application that you want to run");
System.out.println();
System.out.println("Additional parameters are passed to the application");
System.exit(-1);
}
//
// return true if you want to force lazy loading for everything, false if you want to
// force eagerness for everything, null if you don't override this behavior
//
protected Boolean isLazyByDefault() {
return true;
}
}