package com.neverwinterdp.vm;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.beust.jcommander.JCommander;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.neverwinterdp.module.AppModule;
import com.neverwinterdp.registry.Registry;
import com.neverwinterdp.registry.RegistryConfig;
import com.neverwinterdp.registry.RegistryException;
import com.neverwinterdp.util.JSONSerializer;
import com.neverwinterdp.vm.command.VMCommandWatcher;
import com.neverwinterdp.vm.service.VMService;
public class VM {
static {
System.setProperty("java.net.preferIPv4Stack", "true") ;
}
static private Map<String, VM> vms = new ConcurrentHashMap<String, VM>() ;
private Logger logger = LoggerFactory.getLogger(VM.class);
private VMDescriptor descriptor;
private VMStatus vmStatus = VMStatus.INIT;
private Injector vmContainer;
private VMRegistry vmRegistry;
private VMApplicationRunner vmApplicationRunner;
public VM(VMConfig vmConfig) throws Exception {
logger.info("Create VM with VMConfig:");
logger.info(JSONSerializer.INSTANCE.toString(vmConfig));
vmContainer = createVMContainer(vmConfig);
vmRegistry = vmContainer.getInstance(VMRegistry.class);
if(vmConfig.isSelfRegistration()) {
logger.info("Start self registration with the registry");
descriptor = new VMDescriptor(vmConfig);
Registry registry = vmContainer.getInstance(Registry.class);
VMService.register(registry, descriptor);
logger.info("Finish self registration with the registry");
} else {
descriptor = vmRegistry.getVMDescriptor();
}
init();
}
public VM(VMDescriptor vmDescriptor) throws RegistryException {
descriptor = vmDescriptor ;
vmContainer = createVMContainer(vmDescriptor.getVmConfig());
vmRegistry = vmContainer.getInstance(VMRegistry.class);
init();
}
private Injector createVMContainer(final VMConfig vmConfig) {
logger.info("Start createVMContainer(...)");
Map<String, String> props = new HashMap<String, String>();
props.put("vm.registry.allocated.path", VMService.ALLOCATED_PATH + "/" + vmConfig.getName());
AppModule module = new AppModule(props) {
@Override
protected void configure(Map<String, String> props) {
bindInstance(VMConfig.class, vmConfig);
RegistryConfig rConfig = vmConfig.getRegistryConfig();
bindInstance(RegistryConfig.class, rConfig);
try {
bindType(Registry.class, rConfig.getRegistryImplementation());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
};
logger.info("Finish createVMContainer(...)");
return Guice.createInjector(module);
}
public VMStatus getVMStatus() { return this.vmStatus ; }
public VMDescriptor getDescriptor() { return descriptor; }
public VMApp getVMApplication() {
if(vmApplicationRunner == null) return null;
return vmApplicationRunner.vmApplication;
}
public VMRegistry getVMRegistry() { return this.vmRegistry ; }
public void setVMStatus(VMStatus status) throws RegistryException {
vmStatus = status;
vmRegistry.updateStatus(status);
}
public void init() throws RegistryException {
logger.info("Start init(...)");
setVMStatus(VMStatus.INIT);
vmRegistry.addCommandWatcher(new VMCommandWatcher(this));
vmRegistry.createHeartbeat();
logger.info("Finish init(...)");
}
public void run() throws Exception {
logger.info("Start run()");
if(vmApplicationRunner != null) {
throw new Exception("VM Application is already started");
}
VMConfig vmConfig = descriptor.getVmConfig();
Class<VMApp> vmAppType = (Class<VMApp>)Class.forName(vmConfig.getVmApplication()) ;
VMApp vmApp = vmAppType.newInstance();
vmApp.setVM(this);
setVMStatus(VMStatus.RUNNING);
vmApplicationRunner = new VMApplicationRunner(vmApp, vmConfig.getProperties()) ;
vmApplicationRunner.start();
logger.info("Finish run()");
}
public void shutdown() throws Exception {
if(vmApplicationRunner == null || !vmApplicationRunner.isAlive()) return;
Thread thread = new Thread() {
public void run() {
try {
Thread.sleep(500);
if(vmApplicationRunner.vmApplication.isWaittingForShutdown()) {
vmApplicationRunner.vmApplication.notifyShutdown();
} else {
vmApplicationRunner.interrupt();
}
} catch (InterruptedException e) {
}
}
};
thread.start();
}
public synchronized void notifyComplete() {
notifyAll();
}
public synchronized void waitForComplete() throws InterruptedException {
wait();
}
public class VMApplicationRunner extends Thread {
VMApp vmApplication;
Map<String, String> properties;
public VMApplicationRunner(VMApp vmApplication, Map<String, String> props) {
this.vmApplication = vmApplication;
this.properties = props;
}
public void run() {
try {
vmApplication.run();
} catch (InterruptedException e) {
} catch (Exception e) {
logger.error("Error in vm application", e);
} finally {
try {
setVMStatus(VMStatus.TERMINATED);
} catch (RegistryException e) {
logger.error("Error in vm registry", e);
}
try {
vmRegistry.getRegistry().disconnect();
} catch (RegistryException e) {
e.printStackTrace();
}
notifyComplete();
}
}
}
static public VM getVM(VMDescriptor descriptor) {
return vms.get(descriptor.getId());
}
static public void trackVM(VM vm) {
vms.put(vm.getDescriptor().getId(), vm);
}
static public VM run(String[] args) throws Exception {
VMConfig vmConfig = new VMConfig();
new JCommander(vmConfig, args);
VM vm = new VM(vmConfig);
vm.run();
return vm;
}
static public VM run(VMConfig vmConfig) throws Exception {
VM vm = new VM(vmConfig);
vm.run();
return vm;
}
static public void main(String[] args) throws Exception {
System.out.println("VM: main(..) start");
VM vm = run(args);
vm.waitForComplete();
System.out.println("VM: main(..) finish");
}
}