
prompto.runtime.Standalone Maven / Gradle / Ivy
The newest version!
package prompto.runtime;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.esotericsoftware.yamlbeans.document.YamlMapping;
import prompto.code.ICodeStore;
import prompto.code.ImmutableCodeStore;
import prompto.code.MutableCodeStore;
import prompto.compiler.PromptoClassLoader;
import prompto.config.CmdLineConfigurationReader;
import prompto.config.IConfigurationReader;
import prompto.config.IDebugConfiguration;
import prompto.config.IDebugEventAdapterConfiguration;
import prompto.config.IDebugRequestListenerConfiguration;
import prompto.config.IRuntimeConfiguration;
import prompto.config.IStandaloneConfiguration;
import prompto.config.IStoreConfiguration;
import prompto.config.StandaloneConfiguration;
import prompto.config.TempDirectories;
import prompto.config.YamlConfigurationReader;
import prompto.debug.IDebugEventAdapter;
import prompto.debug.IDebugEventAdapterFactory;
import prompto.debug.IDebugRequestListener;
import prompto.debug.IDebugRequestListenerFactory;
import prompto.debug.IDebugger;
import prompto.debug.ProcessDebugger;
import prompto.debug.ProcessDebugger.DebuggedWorker;
import prompto.debug.ProcessStatus;
import prompto.debug.WorkerDebugger;
import prompto.debug.WorkerStatus;
import prompto.debug.event.ConnectedDebugEvent;
import prompto.declaration.AttributeDeclaration;
import prompto.declaration.IDeclaration;
import prompto.error.PromptoError;
import prompto.error.TerminatedError;
import prompto.expression.ValueExpression;
import prompto.expression.IExpression;
import prompto.grammar.Identifier;
import prompto.intrinsic.PromptoDict;
import prompto.intrinsic.PromptoDocument;
import prompto.intrinsic.PromptoVersion;
import prompto.java.JavaIdentifierExpression;
import prompto.libraries.Libraries;
import prompto.reader.YAMLReader;
import prompto.store.AttributeInfo;
import prompto.store.DataStore;
import prompto.store.IStore;
import prompto.store.IStoreFactory;
import prompto.store.memory.MemStore;
import prompto.type.DictType;
import prompto.type.IType;
import prompto.type.ListType;
import prompto.type.TextType;
import prompto.utils.CmdLineParser;
import prompto.utils.IdentifierList;
import prompto.utils.Logger;
import prompto.utils.TypeUtils;
import prompto.value.DictionaryValue;
import prompto.value.IValue;
import prompto.value.TextValue;
import prompto.writer.YAMLWriter;
public abstract class Standalone {
private static Logger logger = new Logger();
private static PromptoClassLoader classLoader;
private static IDebugRequestListener debugRequestListener;
private static IDebugEventAdapter debugEventAdapter;
public static IRuntimeConfiguration configuration;
public static void main(String[] args) throws Throwable {
IStandaloneConfiguration config = loadConfiguration(args);
main(config, null, null);
}
public static void main(IStandaloneConfiguration config, ICodeStore codeStore, IStore dataStore) throws Throwable {
initialize(config, codeStore, dataStore);
run(config);
}
public static void run(IStandaloneConfiguration config) throws Throwable {
IDebugConfiguration debug = config.getDebugConfiguration();
String testMethod = config.getTestMethod();
if(testMethod!=null) {
if(debug!=null)
debugTest(debug, testMethod);
else
runTest(testMethod);
} else {
String mainMethod = config.getMainMethod();
IExpression argsValue = argsToArgValue(config.getArguments());
if(debug!=null)
debugApplication(debug, mainMethod, argsValue);
else
runApplication(mainMethod, argsValue);
}
}
private static IStandaloneConfiguration loadConfiguration(String[] args) throws IOException {
Map argsMap = CmdLineParser.read(args);
IConfigurationReader reader = readerFromArgs(argsMap);
IStandaloneConfiguration config = new StandaloneConfiguration(reader, argsMap);
return config.withRuntimeLibs(()->Libraries.getPromptoLibraries(Libraries.class));
}
public static IConfigurationReader readerFromArgs(Map argsMap) throws IOException {
if(argsMap.containsKey("yamlConfigFile")) {
try(InputStream input = loadYamlData(argsMap.get("yamlConfigFile"))) {
return new YamlConfigurationReader(input);
}
} else
return new CmdLineConfigurationReader(argsMap);
}
private static InputStream loadYamlData(String path) throws FileNotFoundException {
File file = new File(path);
if(file.exists())
return new FileInputStream(file);
InputStream resource = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
if(resource!=null)
return resource;
else
throw new FileNotFoundException(path);
}
public static PromptoDocument getApplicationConfiguration() {
try {
YamlMapping mapping = configuration.toYaml();
String yaml = YAMLWriter.write(mapping);
List> docs = YAMLReader.read(yaml);
return docs.get(0);
} catch(Throwable t) {
throw new InternalError("getApplicationConfiguration failed!", t);
}
}
public static void initialize(IRuntimeConfiguration config) throws Throwable {
initialize(config, null, null);
}
public static void initialize(IRuntimeConfiguration config, ICodeStore codeStore, IStore dataStore) throws Throwable {
configuration = config;
Mode.set(config.getRuntimeMode());
TempDirectories.create();
if(codeStore==null)
codeStore = initializeCodeStore(config);
if(dataStore==null)
dataStore = initializeDataStore(config);
synchronizeSchema(codeStore, dataStore);
}
public static ICodeStore initializeCodeStore(IRuntimeConfiguration config) throws Throwable {
// in SEED scenario, cfg from store is null to ensure latest code from resource is always used
// so we use MemStore to stay consistent, but its content will never be persisted
IStore store = null;
IStoreConfiguration cfg = config.getCodeStoreConfiguration();
if(cfg!=null)
store = IStoreFactory.newStoreFromConfig(cfg);
if(store==null)
store = new MemStore();
boolean isMemStore = store instanceof MemStore;
logger.debug(()->"Using " + ( isMemStore ? "MemStore" : cfg.toString()) + " as code store");
return bootstrapCodeStore(store, config);
}
public static IStore initializeDataStore(IRuntimeConfiguration config) throws Throwable {
IStoreConfiguration cfg = config.getDataStoreConfiguration();
logger.debug(()->"Using " + (cfg==null ? "MemStore" : cfg.toString()) + " as data store");
IStore store = IStoreFactory.newStoreFromConfig(cfg);
return bootstrapDataStore(store);
}
public static PromptoClassLoader getClassLoader() {
return classLoader;
}
private static void runTest(String testMethod) {
runTest(ApplicationContext.get(), testMethod);
}
private static void runTest(Context context, String testMethod) {
try {
if("all".equals(testMethod))
Interpreter.interpretTests(context);
else
Interpreter.interpretTest(context, new Identifier(testMethod), true);
} finally {
context.notifyCompleted();
}
}
private static void debugTest(IDebugConfiguration debug, String testMethod) throws Throwable {
Context local = null;
try {
ProcessDebugger debugger = startProcessDebugger(debug);
local = ApplicationContext.get().newLocalContext();
wireProcessDebugger(debugger, local);
runTest(local, testMethod);
} catch(TerminatedError e) {
if(local!=null)
local.notifyCompleted();
} finally {
stopProcessDebugger();
}
}
private static void runApplication(String mainMethod, IExpression args) {
runApplication(ApplicationContext.get(), mainMethod, args);
}
private static void runApplication(Context context, String mainMethod, IExpression args) {
try {
// TODO use args
Interpreter.interpretMethod(context, new Identifier(mainMethod), "");
} finally {
context.notifyCompleted();
}
}
private static void debugApplication(IDebugConfiguration debug, String mainMethod, IExpression args) throws Throwable {
Context local = null;
try {
ProcessDebugger debugger = startProcessDebugger(debug);
local = ApplicationContext.get().newLocalContext();
wireProcessDebugger(debugger, local);
runApplication(local, mainMethod, args);
} catch(TerminatedError e) {
if(local!=null)
local.notifyCompleted();
} finally {
stopProcessDebugger();
}
}
public static IDebugEventAdapter getDebugEventAdapter() {
return debugEventAdapter;
}
public static IDebugRequestListener getDebugRequestListener() {
return debugRequestListener;
}
public static ProcessDebugger startProcessDebugger(IDebugConfiguration config) throws Throwable {
// wire adapter which will send events to client
debugEventAdapter = createDebugEventAdapter(config);
ProcessDebugger processDebugger = ProcessDebugger.createInstance(ApplicationContext.get());
processDebugger.setListener(debugEventAdapter);
// wire listener which will receive requests from client
debugRequestListener = createDebugRequestListener(config, processDebugger);
ConnectedDebugEvent connected = debugRequestListener.startListening();
debugEventAdapter.onConnectedEvent(connected);
return processDebugger;
}
public static void wireProcessDebugger(ProcessDebugger processDebugger, Context context) {
logger.info(()->"Wiring process debugger...");
// wire context to debugger
WorkerDebugger workerDebugger = startWorkerDebugger(Thread.currentThread(), context);
processDebugger.setProcessStatus(ProcessStatus.PROCESS_RUNNING);
// step in start method by default
// TODO: make this configurable
workerDebugger.stepInto();
logger.info(()->"Process debugger wired.");
}
public static void stopProcessDebugger() {
ProcessDebugger processDebugger = ProcessDebugger.getInstance();
processDebugger.setProcessStatus(ProcessStatus.PROCESS_TERMINATING);
debugEventAdapter.onProcessTerminatedEvent();
processDebugger.setProcessStatus(ProcessStatus.PROCESS_TERMINATED);
debugRequestListener.stopListening();
}
public static WorkerDebugger startWorkerDebugger(Thread thread, Context context) {
WorkerDebugger workerDebugger = new WorkerDebugger();
ProcessDebugger.getInstance().register(Thread.currentThread(), workerDebugger);
debugEventAdapter.onWorkerStartedEvent(DebuggedWorker.wrap(Thread.currentThread()));
workerDebugger.setListener(debugEventAdapter);
context.setDebugger(workerDebugger);
workerDebugger.setWorkerStatus(WorkerStatus.WORKER_RUNNING);
return workerDebugger;
}
public static IDebugRequestListener createDebugRequestListener(IDebugConfiguration cfg, IDebugger debugger) throws Throwable {
IDebugRequestListenerConfiguration config = cfg.getRequestListenerConfiguration();
if(config==null)
throw new RuntimeException("Missing requestListener in debug config!");
return IDebugRequestListenerFactory.newListenerFromConfig(config, debugger);
}
public static IDebugEventAdapter createDebugEventAdapter(IDebugConfiguration cfg) throws Throwable {
IDebugEventAdapterConfiguration config = cfg.getEventAdapterConfiguration();
if(config==null)
throw new RuntimeException("Missing eventAdapter in debug config!");
return IDebugEventAdapterFactory.newAdapterFromConfig(config);
}
public static IExpression argsToArgValue(Map args) {
PromptoDict dict = new PromptoDict<>(true);
for(Map.Entry e : args.entrySet())
dict.put(new TextValue(e.getKey()),new TextValue(e.getValue()));
return new ValueExpression(new DictType(TextType.instance()), new DictionaryValue(TextType.instance(), dict));
}
public static void showHelp(String application, String test, PromptoVersion version) {
if(application==null && test==null)
logger.info(()->"Missing argument: -application or -test");
if(version.equals(PromptoVersion.LATEST))
logger.info(()->"Additional argument: -version (optional)");
}
public static IStore bootstrapDataStore(IStore store) throws Exception {
DataStore.setGlobal(store);
DataStore.useGlobal();
return store;
}
public static void synchronizeSchema(ICodeStore codeStore, IStore dataStore) throws PromptoError {
logger.info(()->"Initializing schema...");
Map columns = getMinimalDataColumns(dataStore);
codeStore.collectStorableAttributes(columns);
Function locator = id->{
Iterable found = codeStore.fetchDeclarations(id.toString());
if(found==null)
return null;
Iterator decls = found.iterator();
return decls.hasNext() ? decls.next() : null;
};
Context context = Context.newGlobalsContext();
List infos = columns.values().stream()
.map((c)->c.getAttributeInfo(context, locator))
.filter(Objects::nonNull)
.collect(Collectors.toList());
dataStore.createOrUpdateAttributes(infos);
logger.info(()->"Schema successfully initialized.");
}
public static ICodeStore bootstrapCodeStore(IStore store, IRuntimeConfiguration config) throws Exception {
logger.info(()->"Initializing class loader " + ( Mode.get()==Mode.UNITTEST ? "in test mode" : "") + "...");
classLoader = PromptoClassLoader.initialize(ApplicationContext.init());
JavaIdentifierExpression.registerAddOns(config.getAddOnURLs(), classLoader);
logger.info(()->"Class loader initialized.");
logger.info(()->"Bootstrapping prompto...");
ICodeStore runtime = config.getRuntimeLibs() == null ? null : ImmutableCodeStore.bootstrapRuntime(config.getRuntimeLibs());
ICodeStore codeStore = newQueryableCodeStore(store, runtime, config);
codeStore.upgradeIfRequired();
ICodeStore.setInstance(codeStore);
logger.info(()->"Bootstrapping successful.");
codeStore.setMainModule(config.getApplicationName(), config.getApplicationVersion());
return codeStore;
}
private static ICodeStore newQueryableCodeStore(IStore store, ICodeStore runtime, IRuntimeConfiguration config) {
return new MutableCodeStore(store,
runtime,
config.getApplicationName(),
config.getApplicationVersion(),
config.getAddOnURLs(),
config.getResourceURLs());
}
public static Map getMinimalDataColumns(IStore dataStore) {
Map columns = new HashMap();
// attributes with reserved names, the below declarations will be used
IType dbIdIType = TypeUtils.typeToIType(dataStore.getNativeDbIdClass());
columns.put(IStore.dbIdName, new AttributeDeclaration(new Identifier(IStore.dbIdName), dbIdIType).withStorable(true));
columns.put("category", new AttributeDeclaration(new Identifier("category"),
new ListType(TextType.instance()), new IdentifierList(new Identifier("key"))));
return columns;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy