package org.myrobotlab.service; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; import java.lang.reflect.Method; import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.URI; import java.net.URL; import java.net.UnknownHostException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.TimeZone; import java.util.TreeMap; import org.myrobotlab.cmdline.CmdLine; import org.myrobotlab.codec.CodecUtils; import org.myrobotlab.framework.Instantiator; import org.myrobotlab.framework.MRLListener; import org.myrobotlab.framework.Message; import org.myrobotlab.framework.MessageListener; import org.myrobotlab.framework.MethodEntry; import org.myrobotlab.framework.Platform; import org.myrobotlab.framework.Service; import org.myrobotlab.framework.ServiceEnvironment; import org.myrobotlab.framework.ServiceType; import org.myrobotlab.framework.Status; import org.myrobotlab.framework.repo.Repo; import org.myrobotlab.framework.repo.ServiceData; import org.myrobotlab.io.FileIO; import org.myrobotlab.logging.Appender; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.logging.Logging; import org.myrobotlab.logging.LoggingFactory; import org.myrobotlab.net.HttpRequest; import org.myrobotlab.service.interfaces.Gateway; import org.myrobotlab.service.interfaces.RepoInstallListener; import org.myrobotlab.service.interfaces.ServiceInterface; import org.myrobotlab.string.StringUtil; import org.slf4j.Logger; /** * Runtime is responsible for the creation and removal of all Services and the * associated static registries It maintains state information regarding * possible & running local Services It maintains state information regarding * foreign Runtimes It is a singleton and should be the only service of Runtime * running in a process The host and registry maps are used in routing * communication to the appropriate service (be it local or remote) It will be * the first Service created It also wraps the real JVM Runtime object. * * TODO - get last args & better restart (with Agent possibly?) * * RuntimeMXBean - scares me - but the stackTrace is clever RuntimeMXBean * runtimeMxBean = ManagementFactory.getRuntimeMXBean(); List<String> arguments * = runtimeMxBean.getInputArguments() * * final StackTraceElement[] stackTrace = * Thread.currentThread().getStackTrace(); final String mainClassName = * stackTrace[stackTrace.length - 1].getClassName(); * * TODO - add check for 64 bit OS & 32 bit JVM :( * */ public class Runtime extends Service implements MessageListener, RepoInstallListener { final static private long serialVersionUID = 1L; /** * instances of MRL - keyed with an instance key URI format is * mrl://gateway/(protocol key) */ /** * environments of running mrl instances - the null environment is the * current local */ static private final Map<URI, ServiceEnvironment> environments = new HashMap<URI, ServiceEnvironment>(); /** * a registry of all services regardless of which environment they came from * - each must have a unique name */ static private final TreeMap<String, ServiceInterface> registry = new TreeMap<String, ServiceInterface>(); /** * map to hide methods we are not interested in */ static private HashSet<String> hideMethods = new HashSet<String>(); static private boolean needsRestart = false; static private String runtimeName; static private Date startDate = new Date(); // DEPRECATED - use Service Timer // private boolean checkForUpdatesOnStart = true; // private boolean autoRestartAfterUpdate = false; static private boolean autoAcceptLicense = true; // at the moment /** * the local repo of this machine - it should not be static as other foreign * repos will come in with other Runtimes from other machines. */ private Repo repo = Repo.getLocalInstance(); private ServiceData serviceData = ServiceData.getLocalInstance(); private Platform platform = Platform.getLocalInstance(); private static long uniqueID = new Random(System.currentTimeMillis()).nextLong(); public final static Logger log = LoggerFactory.getLogger(Runtime.class); /** * Object used to synchronize initializing this singleton. */ transient private static final Object instanceLockObject = new Object(); /** * The singleton of this class. */ transient private static Runtime runtime = null; private List<String> jvmArgs; private List<String> args; /** * * initially I thought that is would be a good idea to dynamically load * Services and append their definitions to the class path. This would * "theoretically" be done with ivy to get/download the appropriate * dependent jars from the repo. Then use a custom ClassLoader to load the * new service. * * Ivy works for downloading the appropriate jars & artifacts However, the * ClassLoader became very problematic * * There is much mis-information around ClassLoaders. The most knowledgeable * article I have found has been this one : * http://blogs.oracle.com/sundararajan * /entry/understanding_java_class_loading * * Overall it became a huge PITA with really very little reward. The * consequence is all Services' dependencies and categories are defined here * rather than the appropriate Service class. * * @return */ // private boolean shutdownAfterUpdate = false; static transient Cli cli; /** * global startingArgs - whatever came into main each runtime will have its * individual copy */ static private String[] globalArgs; static private CmdLine cmdline = null; /** * Returns the number of processors available to the Java virtual machine. * * @return */ public static final int availableProcessors() { return java.lang.Runtime.getRuntime().availableProcessors(); } public static ArrayList<String> buildMLRCommandLine(HashMap<String, String> services, String runtimeName) { ArrayList<String> ret = new ArrayList<String>(); Platform platform = Platform.getLocalInstance(); // library path String classpath = String.format("%s.%s.%s", platform.getArch(), platform.getBitness(), platform.getOS()); String libraryPath = String.format("-Djava.library.path=\"./libraries/native/%s\"", classpath); ret.add(libraryPath); // class path String systemClassPath = System.getProperty("java.class.path"); ret.add("-classpath"); String classPath = String.format("./libraries/jar/*%1$s./libraries/jar/%2$s/*%1$s%3$s", platform.getClassPathSeperator(), classpath, systemClassPath); ret.add(classPath); ret.add("org.myrobotlab.service.Runtime"); // ----- application level params -------------- // services if (services.size() > 0) { ret.add("-service"); for (Map.Entry<String, String> o : services.entrySet()) { ret.add(o.getKey()); ret.add(o.getValue()); } } // runtime name if (runtimeName != null) { ret.add("-runtimeName"); ret.add(runtimeName); } // logLevel // logToConsole // ret.add("-logToConsole"); return ret; } /** * * @param name * @param type * @return */ static public synchronized ServiceInterface create(String name, String type) { String fullTypeName; if (name.indexOf("/") != -1) { throw new IllegalArgumentException(String.format("can not have forward slash / in name %s", name)); } if (type.indexOf(".") == -1) { fullTypeName = String.format("org.myrobotlab.service.%s", type); } else { fullTypeName = type; } return createService(name, fullTypeName); } static public ServiceInterface createAndStart(String name, String type) { ServiceInterface s = null; // framework level catch of all startServices // we will catch it here and log it with a stack trace // its not a good idea to let exceptions propegate higher - because // logging format can get challenging (Python trace-back) or they may // be completely lost - this is the last level the error can be handled // before // going into the unkown - so we catch it ! try { s = create(name, type); s.startService(); } catch (Exception e) { String error = e.getMessage(); if (error == null) { error = "error"; } Runtime.getInstance().error(String.format("createAndStart(%s, %s) %s", name, type, error)); Logging.logError(e); } return s; } /** * creates and starts service from a cmd line object * * @param cmdline * data object from the cmd line */ public final static void createAndStartServices(CmdLine cmdline) { log.info(String.format("createAndStartServices service count %1$d", cmdline.getArgumentCount("-service") / 2)); if (cmdline.getArgumentCount("-service") > 0 && cmdline.getArgumentCount("-service") % 2 == 0) { for (int i = 0; i < cmdline.getArgumentCount("-service"); i += 2) { log.info(String.format("attempting to invoke : org.myrobotlab.service.%1$s named %2$s", cmdline.getSafeArgument("-service", i + 1, ""), cmdline.getSafeArgument("-service", i, ""))); String name = cmdline.getSafeArgument("-service", i, ""); String type = cmdline.getSafeArgument("-service", i + 1, ""); ServiceInterface s = Runtime.create(name, type); if (s != null) { try { s.startService(); } catch (Exception e) { runtime.error(e.getMessage()); Logging.logError(e); } } else { runtime.error(String.format("could not create service %1$s %2$s", name, type)); } } return; } /* * LIST ??? * * else if (cmdline.hasSwitch("-list")) { Runtime runtime = * Runtime.getInstance(); if (runtime == null) { * * } else { log.info(getServiceTypeNames()); } return; } */ mainHelp(); } /** * @param name * @param cls * @return */ static public synchronized ServiceInterface createService(String name, String fullTypeName) { log.info(String.format("Runtime.createService %s", name)); if (name == null || name.length() == 0 || fullTypeName == null || fullTypeName.length() == 0) { log.error("{} not a type or {} not defined ", fullTypeName, name); return null; } ServiceInterface sw = Runtime.getService(name); if (sw != null) { log.debug("service {} already exists", name); return sw; } try { if (log.isDebugEnabled()) { // TODO - determine if there have been new classes added from // ivy --> Boot Classloader --> Ext ClassLoader --> System // ClassLoader // http://blog.jamesdbloom.com/JVMInternals.html log.debug("ABOUT TO LOAD CLASS"); log.debug("loader for this class " + Runtime.class.getClassLoader().getClass().getCanonicalName()); log.debug("parent " + Runtime.class.getClassLoader().getParent().getClass().getCanonicalName()); log.debug("system class loader " + ClassLoader.getSystemClassLoader()); log.debug("parent should be null" + ClassLoader.getSystemClassLoader().getParent().getClass().getCanonicalName()); log.debug("thread context " + Thread.currentThread().getContextClassLoader().getClass().getCanonicalName()); log.debug("thread context parent " + Thread.currentThread().getContextClassLoader().getParent().getClass().getCanonicalName()); } Repo repo = Runtime.getInstance().getRepo(); if (!repo.isServiceTypeInstalled(fullTypeName)) { log.error(String.format("%s is not installed", fullTypeName)); if (autoAcceptLicense) { repo.install(fullTypeName); } } // create an instance Object newService = Instantiator.getNewInstance(fullTypeName, name); log.debug("returning {}", fullTypeName); return (Service) newService; } catch (Exception e) { Logging.logError(e); } return null; } /** * a method which returns a xml representation of all the listeners and * routes in the runtime system * * @return */ public static String dumpNotifyEntries() { ServiceEnvironment se = getLocalServices(); Map<String, ServiceInterface> sorted = new TreeMap<String, ServiceInterface>(se.serviceDirectory); Iterator<String> it = sorted.keySet().iterator(); String serviceName; ServiceInterface sw; String n; ArrayList<MRLListener> nes; MRLListener listener; StringBuffer sb = new StringBuffer().append("<NotifyEntries>"); while (it.hasNext()) { serviceName = it.next(); sw = sorted.get(serviceName); sb.append("<service name=\"").append(sw.getName()).append("\" serviceEnironment=\"").append(sw.getInstanceId()).append("\">"); ArrayList<String> nlks = sw.getNotifyListKeySet(); if (nlks != null) { Iterator<String> nit = nlks.iterator(); while (nit.hasNext()) { n = nit.next(); sb.append("<addListener map=\"").append(n).append("\">"); nes = sw.getNotifyList(n); for (int i = 0; i < nes.size(); ++i) { listener = nes.get(i); sb.append("<MRLListener outMethod=\"").append(listener.topicMethod).append("\" name=\"").append(listener.callbackName).append("\" inMethod=\"") .append(listener.callbackMethod).append("\" />"); } sb.append("</addListener>"); } } sb.append("</service>"); } sb.append("</NotifyEntries>"); return sb.toString(); } public static String dump() { try { FileOutputStream dump = new FileOutputStream("environments.json"); dump.write(CodecUtils.toJson(environments).getBytes()); dump.close(); dump = new FileOutputStream("registry.json"); dump.write(CodecUtils.toJson(registry).getBytes()); dump.close(); StringBuffer sb = new StringBuffer().append("\ninstances:\n"); Map<URI, ServiceEnvironment> sorted = environments; Iterator<URI> hkeys = sorted.keySet().iterator(); URI url; ServiceEnvironment se; Iterator<String> it2; String serviceName; ServiceInterface sw; while (hkeys.hasNext()) { url = hkeys.next(); se = environments.get(url); sb.append("\t").append(url); // Service Environment Map<String, ServiceInterface> sorted2 = new TreeMap<String, ServiceInterface>(se.serviceDirectory); it2 = sorted2.keySet().iterator(); while (it2.hasNext()) { serviceName = it2.next(); sw = sorted2.get(serviceName); sb.append("\t\t").append(serviceName); sb.append("\n"); } } sb.append("\nregistry:"); Map<String, ServiceInterface> sorted3 = new TreeMap<String, ServiceInterface>(registry); Iterator<String> rkeys = sorted3.keySet().iterator(); while (rkeys.hasNext()) { serviceName = rkeys.next(); sw = sorted3.get(serviceName); sb.append("\n").append(serviceName).append(" ").append(sw.getInstanceId()); } sb.toString(); FileIO.toFile(String.format("serviceRegistry.%s.txt", runtime.getName()), sb.toString()); FileIO.toFile(String.format("notifyEntries.%s.xml", runtime.getName()), Runtime.dumpNotifyEntries()); return sb.toString(); } catch (Exception e) { Logging.logError(e); } return null; } /** * big hammer - exits no ifs ands or butts */ public static final void exit() { exit(-1); } /** * Terminates the currently running Java virtual machine by initiating its * shutdown sequence. This method never returns normally. The argument * serves as a status code; by convention, a nonzero status code indicates * abnormal termination * * @param status */ public static final void exit(int status) { try { releaseAll(); } catch (Exception e) { Logging.logError(e); } try { java.lang.Runtime.getRuntime().exit(status); } catch (Exception e) { Logging.logError(e); } // // In unusual situations, System.exit(int) might not actually stop the // program. // Runtime.getRuntime().halt(int) on the other hand, always does. java.lang.Runtime.getRuntime().halt(status); } static public boolean fromAgent() { return cmdline.containsKey("-fromAgent"); } /** * Runs the garbage collector. */ public static final void gc() { java.lang.Runtime.getRuntime().gc(); } public static Cli getCli() { if (cli == null) { cli = startCli(); } return cli; } /** * tricky way of getting static data "static" assumes you talking about * "this" Runtime and no other transported/networked/serialized Runtime .. * and this way Runtime == this instance's runtime ! * * @return */ static public CmdLine getCMDLine() { return cmdline; } /** * although "fragile" since it relies on a external source - its useful to * find the external ip address of NAT'd systems * * @return external or routers ip * @throws Exception */ public static String getExternalIp() throws Exception { URL whatismyip = new URL("http://checkip.amazonaws.com"); BufferedReader in = null; try { in = new BufferedReader(new InputStreamReader(whatismyip.openStream())); String ip = in.readLine(); return ip; } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * Returns the amount of free memory in the Java Virtual Machine. Calling * the gc method may result in increasing the value returned by freeMemory. * * @return */ public static final long getFreeMemory() { return java.lang.Runtime.getRuntime().freeMemory(); } static public String[] getGlobalArgs() { return globalArgs; } /** * Get a handle to the Runtime singleton. * * @return the Runtime */ public static Runtime getInstance() { if (runtime == null) { synchronized (instanceLockObject) { if (runtime == null) { /* * Well that didn't work the way I wanted it to... :P * Thread.setDefaultUncaughtExceptionHandler(new * Thread.UncaughtExceptionHandler() { * * @Override public void uncaughtException(Thread t, * Throwable e) { //log.info(t.getName() + ": " + * e); log.error(String.format( * "============ WHOOP WHOOP WHOOP WHOOP WHOOP WHOOP Thread %s threw %s ============" * , t.getName(), e.getMessage())); // MyWorker worker = new * MyWorker(); // worker.start(); } }); */ if (runtimeName == null) { runtimeName = "runtime"; } runtime = new Runtime(runtimeName); Repo.getLocalInstance().addStatusListener(runtime); } } } return runtime; } static public List<String> getJVMArgs() { RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean(); return runtimeMxBean.getInputArguments(); } /** * gets all non-loopback, active, non-virtual ip addresses * * @return list of local ipv4 IP addresses * @throws SocketException * @throws UnknownHostException */ static public List<String> getLocalAddresses() { log.info("getLocalAddresses"); ArrayList<String> ret = new ArrayList<String>(); try { Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); while (interfaces.hasMoreElements()) { NetworkInterface current = interfaces.nextElement(); // log.info(current); if (!current.isUp() || current.isLoopback() || current.isVirtual()) { log.info("skipping interface is down, a loopback or virtual"); continue; } Enumeration<InetAddress> addresses = current.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress currentAddress = addresses.nextElement(); if (!(currentAddress instanceof Inet4Address)) { log.info("not ipv4 skipping"); continue; } if (currentAddress.isLoopbackAddress()) { log.info("skipping loopback address"); continue; } log.info(currentAddress.getHostAddress()); ret.add(currentAddress.getHostAddress()); } } } catch (Exception e) { Logging.logError(e); } return ret; } // @TargetApi(9) static public List<String> getLocalHardwareAddresses() { log.info("getLocalHardwareAddresses"); ArrayList<String> ret = new ArrayList<String>(); try { Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); while (interfaces.hasMoreElements()) { NetworkInterface current = interfaces.nextElement(); byte[] mac = current.getHardwareAddress(); if (mac == null || mac.length == 0) { continue; } String m = StringUtil.bytesToHex(mac); log.info(String.format("mac address : %s", m)); /* * StringBuilder sb = new StringBuilder(); for (int i = 0; i < * mac.length; i++) { sb.append(String.format("%02X%s", mac[i], * (i < mac.length - 1) ? "-" : "")); } */ ret.add(m); log.info("added mac"); } } catch (Exception e) { Logging.logError(e); } log.info("done"); return ret; } /** * * @return */ public static ServiceEnvironment getLocalServices() { if (!environments.containsKey(null)) { runtime.error("local (null) ServiceEnvironment does not exist"); return null; } return environments.get(null); } /** * getLocalServicesForExport returns a filtered map of Service references to * export to another instance of MRL. The objective of filtering may help * resolve functionality, security, or technical issues. For example, the * Dalvik JVM can only run certain Services. It would be error prone to * export a GUIService to a jvm which does not support swing. * * Since the map of Services is made for export - it is NOT a copy but * references * * The filtering is done by Service Type.. although in the future it could * be extended to Service.getName() * * @return * */ public static ServiceEnvironment getLocalServicesForExport() { if (!environments.containsKey(null)) { runtime.error("local (null) ServiceEnvironment does not exist"); return null; } ServiceEnvironment local = environments.get(null); // URI is null but the "acceptor" will fill in the correct URI/ID ServiceEnvironment export = new ServiceEnvironment(null); Iterator<String> it = local.serviceDirectory.keySet().iterator(); String name; ServiceInterface sw; while (it.hasNext()) { name = it.next(); sw = local.serviceDirectory.get(name); if (security == null || security.allowExport(name)) { // log.info(String.format("exporting service: %s of type %s", // name, sw.getServiceType())); export.serviceDirectory.put(name, sw); // FIXME !! make note // when XMPP or Remote // Adapter pull it - // they have to reset // this !! } else { log.info(String.format("security prevents export of %s", name)); continue; } } return export; } /** * FIXME - DEPRECATE - THIS IS NOT "instance" specific info - its Class * definition info - Runtime should return based on ClassName * * @param serviceName * @return */ public static Map<String, MethodEntry> getMethodMap(String serviceName) { if (!registry.containsKey(serviceName)) { runtime.error(String.format("%1$s not in registry - can not return method map", serviceName)); return null; } Map<String, MethodEntry> ret = new TreeMap<String, MethodEntry>(); ServiceInterface sw = registry.get(serviceName); Class<?> c = sw.getClass(); Method[] methods = c.getDeclaredMethods(); Method m; MethodEntry me; String s; for (int i = 0; i < methods.length; ++i) { m = methods[i]; if (hideMethods.contains(m.getName())) { continue; } me = new MethodEntry(m); me.parameterTypes = m.getParameterTypes(); me.returnType = m.getReturnType(); s = me.getSignature(); ret.put(s, me); } return ret; } public static String getPid() { SimpleDateFormat TSFormatter = new SimpleDateFormat("yyyyMMddHHmmssSSS"); final String fallback = TSFormatter.format(new Date()); try { // something like '<pid>@<hostname>', at least in SUN / Oracle JVMs final String jvmName = ManagementFactory.getRuntimeMXBean().getName(); final int index = jvmName.indexOf('@'); if (index < 1) { // part before '@' empty (index = 0) / '@' not found (index = // -1) return fallback; } return Long.toString(Long.parseLong(jvmName.substring(0, index))); } catch (Exception e) { } return fallback; } /** * * @return */ public static Map<String, ServiceInterface> getRegistry() { return registry;// FIXME should return copy } /** * Return the named service - - if name is not null, but service is not * found - return null (for re-entrant Service creation) - if the name IS * null, return Runtime - to support api/getServiceNames - if the is not * null, and service is found - return the Service * * @param name * @return */ public static ServiceInterface getService(String name) { if (name == null || name.length() == 0) { return Runtime.getInstance(); } if (!registry.containsKey(name)) { return null; } else { return registry.get(name); } } /** * * @param url * @return */ public static ServiceEnvironment getEnvironment(URI url) { if (environments.containsKey(url)) { return environments.get(url); // FIXME should return copy } return null; } /** * get all environments * @return */ public static HashMap<URI, ServiceEnvironment> getEnvironments() { return new HashMap<URI, ServiceEnvironment>(environments); } // Reference - cpu utilization // http://www.javaworld.com/javaworld/javaqa/2002-11/01-qa-1108-cpu.html /** * list of currently created services * * @return */ static public String[] getServiceNames() { List<ServiceInterface> si = getServices(); String[] ret = new String[si.size()]; for (int i = 0; i < ret.length; ++i) { ret[i] = si.get(i).getName(); } return ret; } public static List<String> getServiceNamesFromInterface(String interfaze) throws ClassNotFoundException { return getServiceNamesFromInterface(Class.forName(interfaze)); } /** * @param interfaceName * @return service names which match */ public static List<String> getServiceNamesFromInterface(Class<?> interfaze) { ArrayList<String> ret = new ArrayList<String>(); ArrayList<ServiceInterface> services = getServicesFromInterface(interfaze); for (int i = 0; i < services.size(); ++i) { ret.add(services.get(i).getName()); } return ret; } public static List<ServiceInterface> getServices() { List<ServiceInterface> list = new ArrayList<ServiceInterface>(registry.values()); return list; } /** * @param interfaze * @return services which match */ public static ArrayList<ServiceInterface> getServicesFromInterface(Class<?> interfaze) { ArrayList<ServiceInterface> ret = new ArrayList<ServiceInterface>(); Iterator<String> it = registry.keySet().iterator(); String serviceName; ServiceInterface sw; Class<?> c; Class<?>[] interfaces; Class<?> m; while (it.hasNext()) { serviceName = it.next(); sw = registry.get(serviceName); c = sw.getClass(); interfaces = c.getInterfaces(); for (int i = 0; i < interfaces.length; ++i) { m = interfaces[i]; if (m.equals(interfaze)) { ret.add(sw); } } } return ret; } static public Set<Thread> getThreads() { return Thread.getAllStackTraces().keySet(); } /** * dorky pass-throughs to the real JVM Runtime * * @return */ public static final long getTotalMemory() { return java.lang.Runtime.getRuntime().totalMemory(); } /** * attempt to get physical memory from the jvm not supported in all jvms.. * * @return */ static public long getTotalPhysicalMemory() { try { com.sun.management.OperatingSystemMXBean os = (com.sun.management.OperatingSystemMXBean) java.lang.management.ManagementFactory.getOperatingSystemMXBean(); long physicalMemorySize = os.getTotalPhysicalMemorySize(); return physicalMemorySize; } catch (Exception e) { log.error("getTotalPhysicalMemory - threw"); } return 0; } /** * unique id's are need for sendBlocking - to uniquely identify the message * this is a method to support that - it is unique within a process, but not * across processes * * @return a unique id */ public static final synchronized long getUniqueID() { ++uniqueID; return uniqueID; } public static String getUptime() { Date now = new Date(); long diff = now.getTime() - startDate.getTime(); long diffSeconds = diff / 1000 % 60; long diffMinutes = diff / (60 * 1000) % 60; long diffHours = diff / (60 * 60 * 1000) % 24; long diffDays = diff / (24 * 60 * 60 * 1000); StringBuffer sb = new StringBuffer(); sb.append(diffDays).append(" days ").append(diffHours).append(" hours ").append(diffMinutes).append(" minutes ").append(diffSeconds).append(" seconds"); return sb.toString(); } public static String getVersion() { return Platform.getLocalInstance().getVersion();// FileIO.resourceToString("version.txt"); } // FIXME - shouldn't this be in platform ??? public static String getBranch() { return Platform.getLocalInstance().getBranch();// FileIO.resourceToString("branch.txt"); } static public void install() throws ParseException, IOException { // if a runtime exits we'll broadcast we are starting to install ServiceData sd = ServiceData.getLocalInstance(); if (runtime != null) { runtime.onInstallProgress(Repo.createStartStatus("starting installation of %s services", sd.getServiceTypeNames().length)); } Repo.getLocalInstance().install(); // if a runtime exits we'll broadcast we are done installing if (runtime != null) { runtime.onInstallProgress(Repo.createFinishedStatus("finished installing %s", sd.getServiceTypeNames().length)); } } /** * Installs a single Service type. This "should" work even if there is no * Runtime. It can be invoked on the command line without starting a MRL * instance. If a runtime exits it will broadcast events of installation * progress * * @param serviceType * @throws ParseException * @throws IOException */ static public void install(String serviceType) throws ParseException, IOException { // if a runtime exits we'll broadcast we are starting to install if (runtime != null) { runtime.onInstallProgress(Repo.createStartStatus("starting installation of %s", serviceType)); } Repo.getLocalInstance().install(serviceType); // if a runtime exits we'll broadcast we are done installing if (runtime != null) { runtime.onInstallProgress(Repo.createFinishedStatus("finished installing %s", serviceType)); } } /** * broadcast of Service install progress * * @param status * @return */ public Status publishInstallProgress(Status status) { return status; } /** * direct callback from the Repo on installation progress we re-broadcast * this on a topic */ public void onInstallProgress(final Status status) { invoke("publishInstallProgress", status); } static public void invokeCommands(CmdLine cmdline) { int argCount = cmdline.getArgumentCount("-invoke"); if (argCount > 1) { StringBuffer params = new StringBuffer(); ArrayList<String> invokeList = cmdline.getArgumentList("-invoke"); Object[] data = new Object[argCount - 2]; for (int i = 2; i < argCount; ++i) { data[i - 2] = invokeList.get(i); params.append(String.format("%s ", invokeList.get(i))); } String name = cmdline.getArgument("-invoke", 0); String method = cmdline.getArgument("-invoke", 1); log.info(String.format("attempting to invoke : %s.%s(%s)\n", name, method, params.toString())); getInstance().send(name, method, data); } } /** * invoked to confirm with the user it is appropriate to restart now * * @return - the current autoRestartAfterUpdate to put in dialog to (never * ask again) */ /* * public boolean confirmRestart() { needsRestart = true; return * autoRestartAfterUpdate; } */ static public boolean isAgent() { if (cmdline == null) { return false; } return cmdline.containsKey("-isAgent"); } static public boolean isHeadless() { // String awt = "java.awt.GraphicsEnvironment"; // java.awt.GraphicsEnvironment.isHeadless() // String nm = System.getProperty("java.awt.headless"); // should return true if Linux != display String b = System.getProperty("java.awt.headless"); return Boolean.parseBoolean(b); } public static boolean isLocal(String serviceName) { ServiceInterface sw = getService(serviceName); return sw.isLocal(); } /** * check if class is a Runtime class * * @param newService * @return true if class == Runtime.class */ public static boolean isRuntime(Service newService) { return newService.getClass().equals(Runtime.class); } /** * load all configuration from all local services * * @return */ static public boolean loadAll() { boolean ret = true; ServiceEnvironment se = getLocalServices(); Iterator<String> it = se.serviceDirectory.keySet().iterator(); String serviceName; while (it.hasNext()) { serviceName = it.next(); ServiceInterface sw = se.serviceDirectory.get(serviceName); ret &= sw.load(); } return ret; } /** * * @param filename */ public static final void loadLibrary(String filename) { java.lang.Runtime.getRuntime().loadLibrary(filename); } /** * Main starting method of MyRobotLab Parses command line options * * -h help -v version -list jvm args -Dhttp.proxyHost=webproxy * -Dhttp.proxyPort=80 -Dhttps.proxyHost=webproxy -Dhttps.proxyPort=80 * * @param args */ public static void main(String[] args) { System.out.println(Arrays.toString(args)); // global for this process globalArgs = args; // sub-process if there is one cmdline = new CmdLine(args); Logging logging = LoggingFactory.getInstance(); try { logging.setLevel(cmdline.getSafeArgument("-logLevel", 0, "INFO")); if (cmdline.containsKey("-v") || cmdline.containsKey("--version")) { System.out.print(Runtime.getVersion()); return; } if (cmdline.containsKey("-runtimeName")) { runtimeName = cmdline.getSafeArgument("-runtimeName", 0, "MRL"); } /* * Previously the Agent remained running - and would siphon std:in & * std:out if (cmdline.containsKey("-isAgent")) { * logging.addAppender(Appender.IS_AGENT); } else if * (cmdline.containsKey("-fromAgent")) { * logging.addAppender(Appender.FROM_AGENT); } else */ if (cmdline.containsKey("-logToConsole")) { logging.addAppender(Appender.CONSOLE); } else { logging.addAppender(Appender.FILE, String.format("%s.log", runtimeName)); } log.info(cmdline.toString()); if (cmdline.containsKey("-h") || cmdline.containsKey("--help")) { mainHelp(); return; } // logging.addAppender(Appender.CONSOLE); hopefully it still worky // after removing this ! :) if (!cmdline.containsKey("-noCLI")) { Runtime.getInstance(); startCli(); } // LINUX LD_LIBRARY_PATH MUST BE EXPORTED - NO OTHER SOLUTION FOUND // hack to reconcile the different ways os handle and expect // "PATH & LD_LIBRARY_PATH" to be handled // found here - // http://blog.cedarsoft.com/2010/11/setting-java-library-path-programmatically/ // but does not work if (cmdline.containsKey("-install")) { // force all updates ArrayList<String> services = cmdline.getArgumentList("-install"); Repo repo = Repo.getLocalInstance(); if (services.size() == 0) { repo.install(); } else { for (int i = 0; i < services.size(); ++i) { repo.install(services.get(i)); } } shutdown(); return; } if (cmdline.containsKey("-service")) { createAndStartServices(cmdline); } if (cmdline.containsKey("-invoke")) { invokeCommands(cmdline); } } catch (Exception e) { Logging.logError(e); System.out.print(Logging.stackToString(e)); Service.sleep(2000); } } /** * prints help to the console */ static void mainHelp() { System.out.println(String.format("Runtime %s", Runtime.getVersion())); System.out.println("-h --help # help "); System.out.println("-v --version # print version"); System.out.println("-update # update myrobotlab"); System.out.println("-invoke name method [param1 param2 ...] # invoke a method of a service"); System.out.println("-install [ServiceType1 ServiceType2 ...] # install services - if no ServiceTypes are specified install all"); System.out.println("-runtimeName <runtime name> # rename the Runtime service - prevents multiple instance name collisions"); System.out.println("-logToConsole # redirects logging to console"); System.out.println("-logLevel <DEBUG | INFO | WARNING | ERROR> # log level"); System.out.println("-service <name1 Type1 name2 Type2 ...> # create and start list of services, e.g. -service gui GUIService"); System.out.println("example:"); String helpString = "java -Djava.library.path=./libraries/native/x86.32.windows org.myrobotlab.service.Runtime -service webgui WebGui gui GUIService -logLevel INFO -logToConsole"; System.out.println(helpString); } public static String message(String msg) { getInstance().invoke("publishMessage", msg); log.info(msg); return msg; } /** * needsRestart is set by the update process - or some othe method when a * restart is needed. Used by other Services to prepare for restart * * @return needsRestart */ public static boolean needsRestart() { return needsRestart; } public void onState(ServiceInterface updatedService) { log.info("runtime updating registry info for remote service {}", updatedService.getName()); registry.put(updatedService.getName(), updatedService); ServiceEnvironment se = environments.get(updatedService.getInstanceId()); if (se != null) { se.serviceDirectory.put(updatedService.getName(), updatedService); } else { error("onState ServiceEnvironment null"); } } /** * * register - this method enters the service into the registery of services * * * @param url * @param s * @return */ public final static synchronized ServiceInterface register(ServiceInterface s, URI url) { ServiceEnvironment se = null; if (!environments.containsKey(url)) { se = new ServiceEnvironment(url); environments.put(url, se); } else { se = environments.get(url); } /** * register with null data is how initial communication * starts between 2 mrl instances (1st msg) */ if (s == null) { return null; } String name = s.getName(); // REMOTE BROADCAST to all foreign environments // FIXME - Security determines what to export // for each gateway // RELAY - xforwarder name grows remote1.remote2.remote3 .... List<String> remoteGateways = getServiceNamesFromInterface(Gateway.class); for (int ri = 0; ri < remoteGateways.size(); ++ri) { String n = remoteGateways.get(ri); // Communicator gateway = (Communicator)registry.get(n); ServiceInterface gateway = registry.get(n); // for each JVM this gateway is attached too for (Map.Entry<URI, ServiceEnvironment> o : environments.entrySet()) { URI uri = o.getKey(); // if its a foreign JVM & the gateway responsible for the // remote // connection and // the foreign JVM is not the host which this service // originated // from - send it.... if (uri != null && gateway.getName().equals(uri.getHost()) && !uri.equals(s.getInstanceId())) { log.info(String.format("gateway %s sending registration of %s remote to %s", gateway.getName(), name, uri)); // FIXME - Security determines what to export Message msg = runtime.createMessage(null, "register", s); // ((Communicator) gateway).sendRemote(uri, msg); // //mrl://remote2/tcp://127.0.0.1:50488 <-- wrong // sendingRemote is wrong // FIXME - optimize gateway.send(msg) && URI TO URI MAP // IN // RUNTIME !!! gateway.in(msg); } } } // ServiceInterface sw = new ServiceInterface(s, se.accessURL); se.serviceDirectory.put(name, s); // WARNING - SHOULDN'T THIS BE DONE FIRST AVOID DEADLOCK / RACE // CONDITION ???? registry.put(name, s); // FIXME FIXME FIXME FIXME !!!!!! // pre-pend // URI if not NULL !!! if (runtime != null) { runtime.invoke("registered", s); } // new -------- // we want to subscribe to state changes if (!s.isLocal()) { runtime.subscribe(name, "publishState"); } // end new ---- return s; } /** * releases a service - stops the service, its threads, releases its * resources, and removes registry entries * * @param name * of the service to be released * @return whether or not it successfully released the service */ public synchronized static boolean release(String name) { log.info("releasing service {}", name); Runtime rt = getInstance(); if (!registry.containsKey(name)) { rt.error("release could not find %s", name); return false; } ServiceInterface sw = registry.remove(name); if (sw.isLocal()) { sw.stopService(); } ServiceEnvironment se = environments.get(sw.getInstanceId()); se.serviceDirectory.remove(name); rt.invoke("released", sw); log.info("released {}", name); return true; } /** * * @param url * @return */ public static boolean release(URI url) /* release process environment */ { boolean ret = true; ServiceEnvironment se = environments.get(url); if (se == null) { log.warn("attempt to release {} not successful - it does not exist", url); return false; } log.info(String.format("releasing url %1$s", url)); String[] services = se.serviceDirectory.keySet().toArray(new String[se.serviceDirectory.keySet().size()]); String runtimeName = null; ServiceInterface service; for (int i = 0; i < services.length; ++i) { service = registry.get(services[i]); if (service != null && "Runtime".equals(service.getSimpleName())) { runtimeName = service.getName(); log.info(String.format("delaying release of Runtime %1$s", runtimeName)); continue; } ret &= release(services[i]); } if (runtimeName != null) { ret &= release(runtimeName); } return ret; } /** * This does not EXIT(1) !!! releasing just releases all services * * FIXME FIXME FIXME - just call release on each - possibly saving runtime * for last .. send prepareForRelease before releasing * * release all local services * * FIXME - there "should" be an order to releasing the correct way would be * to save the Runtime for last and broadcast all the services being * released * * FIXME - send SHUTDOWN event to all running services with a timeout period * - end with System.exit() FIXME normalize with releaseAllLocal and * releaseAllExcept */ public static void releaseAll() /* local only? YES !!! LOCAL ONLY !! */ { log.debug("releaseAll"); ServiceEnvironment se = environments.get(null); // local services only if (se == null) { log.info("releaseAll called when everything is released, all done here"); return; } Iterator<String> seit = se.serviceDirectory.keySet().iterator(); String serviceName; ServiceInterface sw; seit = se.serviceDirectory.keySet().iterator(); while (seit.hasNext()) { serviceName = seit.next(); sw = se.serviceDirectory.get(serviceName); if (sw == Runtime.getInstance()) { // skipping runtime continue; } log.info(String.format("stopping service %s", serviceName)); if (sw == null) { log.warn("unknown type and/or remote service"); continue; } // runtime.invoke("released", se.serviceDirectory.get(serviceName)); // FIXME DO THIS try { sw.stopService(); // sw.releaseService(); // FIXED ! - releaseService will mod the // maps :P runtime.invoke("released", sw); } catch (Exception e) { runtime.error(String.format("%s threw while stopping", e)); Logging.logError(e); } } runtime.stopService(); log.info("clearing hosts environments"); environments.clear(); log.info("clearing registry"); registry.clear(); // exit () ? } public static void shutdown() { releaseAll(); System.exit(-1); } // ---------------- callback events end ------------- // ---------------- Runtime begin -------------- public static void releaseAllServicesExcept(HashSet<String> saveMe) { log.info("releaseAllServicesExcept"); List<ServiceInterface> list = Runtime.getServices(); for (int i = 0; i < list.size(); ++i) { ServiceInterface si = list.get(i); if (saveMe != null && saveMe.contains(si.getName())) { log.info("leaving {}", si.getName()); continue; } else { si.releaseService(); } } } /** * @param name * - name of Service to be removed and whos resources will be * released */ static public void releaseService(String name) { Runtime.release(name); } // ============== update events begin ============== /** * * should probably be deprecated - currently not used * * @param runBeforeRestart * * will this work on a file lock update? */ static public void restart(Runnable runBeforeRestart) { final java.lang.Runtime r = java.lang.Runtime.getRuntime(); log.info("restart - restart?"); Runtime.releaseAll(); try { // java binary String java = System.getProperty("java.home") + "/bin/java"; // init the command to execute, add the vm args final StringBuffer cmd = new StringBuffer("\"" + java + "\" "); // program main and program arguments String[] mainCommand = globalArgs; // program main is a jar if (mainCommand[0].endsWith(".jar")) { // if it's a jar, add -jar mainJar cmd.append("-jar " + new File(mainCommand[0]).getPath()); } else { // else it's a .class, add the classpath and mainClass cmd.append("-cp \"" + System.getProperty("java.class.path") + "\" " + mainCommand[0]); } // finally add program arguments for (int i = 1; i < mainCommand.length; i++) { cmd.append(" "); cmd.append(mainCommand[i]); } // execute the command in a shutdown hook, to be sure that all the // resources have been disposed before restarting the application r.addShutdownHook(new Thread() { @Override public void run() { try { r.exec(cmd.toString()); } catch (IOException e) { e.printStackTrace(); } } }); // execute some custom code before restarting if (runBeforeRestart != null) { runBeforeRestart.run(); } // exit } catch (Exception ex) { Logging.logError(ex); } System.exit(0); } /** * save all configuration from all local services * * @return */ static public boolean saveAll() { boolean ret = true; ServiceEnvironment se = getLocalServices(); Iterator<String> it = se.serviceDirectory.keySet().iterator(); String serviceName; while (it.hasNext()) { serviceName = it.next(); ServiceInterface sw = se.serviceDirectory.get(serviceName); ret &= sw.save(); } return ret; } public static boolean setJSONPrettyPrinting(boolean b) { return CodecUtils.setJSONPrettyPrinting(b); } public static void setRuntimeName(String inName) { runtimeName = inName; } static public ServiceInterface start(String name, String type) { return createAndStart(name, type); } static public Cli startCli() { cli = (Cli) start("cli", "Cli"); return cli; } static public void stopCli() { if (cli != null) { release(cli.getName()); } } public Runtime(String n) { super(n); synchronized (instanceLockObject) { if (runtime == null) { runtime = this; } } String libararyPath = System.getProperty("java.library.path"); String userDir = System.getProperty("user.dir"); String userHome = System.getProperty("user.home"); String vmName = System.getProperty("java.vm.name"); // TODO this should be a single log statement // http://developer.android.com/reference/java/lang/System.html Date now = new Date(); String format = "yyyy/MM/dd HH:mm:ss"; SimpleDateFormat sdf = new SimpleDateFormat(format); SimpleDateFormat gmtf = new SimpleDateFormat(format); gmtf.setTimeZone(TimeZone.getTimeZone("UTC")); log.info("============== args begin =============="); StringBuffer sb = new StringBuffer(); jvmArgs = getJVMArgs(); args = new ArrayList<String>(); if (globalArgs != null) { for (int i = 0; i < globalArgs.length; ++i) { sb.append(globalArgs[i]); args.add(globalArgs[i]); } } if (jvmArgs != null) { log.info(String.format("jvmArgs %s", Arrays.toString(jvmArgs.toArray()))); } log.info(String.format("args %s", Arrays.toString(args.toArray()))); log.info("============== args end =============="); if (cmdline != null && !cmdline.containsKey("-noEnv")) { log.info("============== env begin =============="); Map<String, String> env = System.getenv(); for (Map.Entry<String, String> entry : env.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); log.info(String.format("%s=%s", key, value)); } log.info("============== env end =============="); } // Platform platform = Platform.getLocalInstance(); log.info("============== normalized =============="); log.info("{} - GMT - {}", sdf.format(now), gmtf.format(now)); log.info("Pid {}", getPid()); log.info("ivy [runtime,{}.{}.{}]", platform.getArch(), platform.getBitness(), platform.getOS()); log.info("os.name [{}] getOS [{}]", System.getProperty("os.name"), platform.getOS()); log.info("os.arch [{}] getArch [{}]", System.getProperty("os.arch"), platform.getArch()); log.info("getBitness [{}]", platform.getBitness()); log.info("java.vm.name [{}] getVMName [{}]", vmName, platform.getVMName()); log.info("version [{}]", Runtime.getVersion()); log.info("root [{}]", FileIO.getRoot()); log.info("cfg dir [{}]", FileIO.getCfgDir()); log.info("sun.arch.data.model [{}]", System.getProperty("sun.arch.data.model")); log.info("============== non-normalized =============="); log.info("java.vm.name [{}]", vmName); log.info("java.vm.version [{}]", System.getProperty("java.vm.version")); log.info("java.vm.vendor [{}]", System.getProperty("java.vm.vendor")); log.info("java.vm.version [{}]", System.getProperty("java.vm.version")); log.info("java.vm.vendor [{}]", System.getProperty("java.runtime.version")); // System.getProperty("pi4j.armhf") log.info("os.version [{}]", System.getProperty("os.version")); log.info("os.version [{}]", System.getProperty("os.version")); log.info("java.home [{}]", System.getProperty("java.home")); log.info("java.class.path [{}]", System.getProperty("java.class.path")); log.info("java.library.path [{}]", libararyPath); log.info("user.dir [{}]", userDir); log.info("user.home [{}]", userHome); log.info("total mem [{}] Mb", Runtime.getTotalMemory() / 1048576); log.info("total free [{}] Mb", Runtime.getFreeMemory() / 1048576); log.info("total physical mem [{}] Mb", Runtime.getTotalPhysicalMemory() / 1048576); log.info("getting local repo"); Repo.getLocalInstance().addStatusListener(this); hideMethods.add("main"); hideMethods.add("loadDefaultConfiguration"); hideMethods.add("getDescription"); hideMethods.add("run"); hideMethods.add("access$0"); // TODO - check for updates on startup ??? // starting this try { startService(); } catch (Exception e) { error("OMG Runtime won't start GAME OVER ! :( %s", e.getMessage()); Logging.logError(e); } } /** * publishing event - since checkForUpdates may take a while */ public void checkingForUpdates() { log.info("checking for updates"); } static public String getInputAsString(InputStream is) { try (java.util.Scanner s = new java.util.Scanner(is)) { return s.useDelimiter("\\A").hasNext() ? s.next() : ""; } } // ---------- Java Runtime wrapper functions begin -------- /** * Executes the specified command and arguments in a separate process. * Returns the exit value for the subprocess. * * @param params * @return */ static public String exec(String program) { return execute(program, null, null, null, null); } /** * publishing point of Ivy sub system - sends event failedDependency when * the retrieve report for a Service fails * * @param dep * @return */ public String failedDependency(String dep) { return dep; } /** * returns version string of MyRobotLab * * @return */ public String getLocalVersion() { return getVersion(null); } // FIXME - you don't need that many "typed" messages - resolve, // resolveError, ... etc // just use & parse "message" public Platform getPlatform() { return platform; } /** * returns the platform type of a remote system * * @param uri * - the access uri of the remote system * @return Platform description */ public Platform getPlatform(URI uri) { ServiceEnvironment local = environments.get(uri); if (local != null) { return local.platform; } error("can't get local platform in service environment"); return null; } public Repo getRepo() { return repo; } /** * Gets the current total number of services registered services. This is * the number of services in all Service Environments * * @return total number of services */ public int getServiceCount() { int cnt = 0; Iterator<URI> it = environments.keySet().iterator(); ServiceEnvironment se; Iterator<String> it2; while (it.hasNext()) { se = environments.get(it.next()); it2 = se.serviceDirectory.keySet().iterator(); while (it2.hasNext()) { ++cnt; it2.next(); } } return cnt; } // ============== configuration begin ============== /** * * @return */ public int getEnvironmentCount() { return environments.size(); } /** * Returns an array of all the simple type names of all the possible * services. The data originates from the repo's serviceData.xml file * https:/ * /code.google.com/p/myrobotlab/source/browse/trunk/myrobotlab/thirdParty * /repo/serviceData.xml * * There is a local one distributed with the install zip When a "update" is * forced, MRL will try to download the latest copy from the repo. * * The serviceData.xml lists all service types, dependencies, categories and * other relevant information regarding service creation * * @return */ public String[] getServiceTypeNames() { return getServiceTypeNames("all"); } // ============== configuration end ============== /** * publishing event to get the possible services currently available * * @param filter * @return */ public String[] getServiceTypeNames(String filter) { return serviceData.getServiceTypeNames(filter); } /** * returns version string of MyRobotLab instance based on uri e.g : uri * mrl://10.5.3.1:7777 may be a remote instance null uri is local * * @param uri * - key of ServiceEnvironment * @return version string */ public String getVersion(URI uri) { ServiceEnvironment local = environments.get(uri); if (local != null) { return local.platform.getVersion(); } error("can't get local version in service environment"); return null; } /** * event fired when a new artifact is download * * @param module * @return */ public String newArtifactsDownloaded(String module) { return module; } // FIXME THIS IS NOT NORMALIZED !!! static public Status noWorky(String userId) { Status status = null; try { String retStr = HttpRequest.postFile("http://myrobotlab.org/myrobotlab_log/postLogFile.php", userId, "file", new File(LoggingFactory.getLogFileName())); if (retStr.contains("Upload:")) { log.info("noWorky successfully sent - our crack team of experts will check it out !"); status = Status.info("no worky sent"); } else { status = Status.error("could not send"); } } catch (Exception e) { log.error("the noWorky didn't worky !"); status = Status.error(e); } // this makes the 'static' of this method pointless // perhaps the webgui should invoke rather than call directly .. :P Runtime.getInstance().invoke("publishNoWorky", status); return status; } /** * published results of sending a noWorky * * @param status * @return */ static public Status publishNoWorky(Status status) { return status; } // -------- network begin ------------------------ /** * this method is an event notifier that there were updates found */ public ServiceData proposedUpdates(ServiceData si) { return si; } public String publishMessage(String msg) { return msg; } // -------- network end ------------------------ // http://stackoverflow.com/questions/16610525/how-to-determine-if-graphicsenvironment-exists // FIXME - this is important in the future @Override public void receive(Message msg) { // TODO Auto-generated method stub } // ---------------- callback events begin ------------- /** * registration event * * @param path * - the name of the Service which was successfully registered * @return */ public ServiceInterface registered(ServiceInterface sw) { return sw; } /** * release event * * @param path * - the name of the Service which was successfully released * @return */ public ServiceInterface released(ServiceInterface sw) { return sw; } /** * published events * * @param className * @return */ public String resolveBegin(String className) { return className; } public void resolveEnd() { } /** * * @param errors * @return */ public List<String> resolveError(List<String> errors) { return errors; } /** * * @param className * @return */ public String resolveSuccess(String className) { return className; } /** * FIXME - need to extend - communication to Agent ??? process request * restart ??? * * restart occurs after applying updates - user or config data needs to be * examined and see if its an appropriate time to restart - if it is the * spawnBootstrap method will be called and bootstrap.jar will go through * its sequence to update myrobotlab.jar */ public void restart() { try { info("restarting"); // TODO - timeout release .releaseAll nice ? - check or re-implement Runtime.releaseAll(); // Bootstrap.spawn(args.toArray(new String[args.size()])); System.exit(0); // shutdown / exit } catch (Exception e) { Logging.logError(e); } } /** * Runtime's setLogLevel will set the root log level if its called from a * service - it will only set that Service type's log level * */ @Override public String setLogLevel(String level) { Logging logging = LoggingFactory.getInstance(); logging.setLevel(level); return level; } /** * Stops all service-related running items. This releases the singleton * referenced by this class, but it does not guarantee that the old service * will be GC'd. FYI - if stopServices does not remove INSTANCE - it is not * re-entrant in junit tests */ @Override public void stopService() { super.stopService(); runtime = null; } public static void clearErrors() { ServiceEnvironment se = getLocalServices(); for (String name : se.serviceDirectory.keySet()) { se.serviceDirectory.get(name).clearLastError(); } } public static boolean hasErrors() { ServiceEnvironment se = getLocalServices(); for (String name : se.serviceDirectory.keySet()) { if (se.serviceDirectory.get(name).hasError()) { return true; } } return false; } /** * remove all subscriptions from all local Services */ static public void removeAllSubscriptions() { ServiceEnvironment se = getEnvironment(null); Set<String> keys = se.serviceDirectory.keySet(); for (String name : keys) { ServiceInterface si = getService(name); ArrayList<String> nlks = si.getNotifyListKeySet(); for (int i = 0; i < nlks.size(); ++i) { si.getOutbox().notifyList.clear(); } } } public static ArrayList<Status> getErrors() { ArrayList<Status> stati = new ArrayList<Status>(); ServiceEnvironment se = getLocalServices(); for (String name : se.serviceDirectory.keySet()) { Status status = se.serviceDirectory.get(name).getLastError(); if (status != null && status.isError()) { log.info(status.toString()); stati.add(status); } } return stati; } public static void broadcastStates() { ServiceEnvironment se = getLocalServices(); for (String name : se.serviceDirectory.keySet()) { se.serviceDirectory.get(name).broadcastState(); } } public static Runtime get() { return Runtime.getInstance(); } public static String getRuntimeName() { return Runtime.getInstance().getName(); } static public String execute(String... args){ if (args == null || args.length == 0){ log.error("execute invalid number of args"); return null; } String program = args[0]; List<String> list = null; if (args.length > 1){ list = new ArrayList<String>(); for (int i = 1; i < args.length; ++i){ list.add(args[i]); } } return execute(program, list, null, null, null); } /** * FIXME - return a POJO - because there are lots of downstream which would want different parts of the results * FIXME - stream gobbler reconciled - threaded stream consumption * FIXME - watchdog - a good idea * FIXME - most common use case would be returning a string i would think * FIXME - ProcessData & ProcessData2 reconciled * * @param program * @param args * @param workingDir * @param additionalEnv * @return */ static public String execute(String program, List<String> args, String workingDir, Map<String,String> additionalEnv, Boolean block) { log.info("execToString(\"{} {}\")", program, args); ArrayList<String> command = new ArrayList<String>(); command.add(program); if (args != null) { for (String arg : args) { command.add(arg); } } Integer exitValue = null; ProcessBuilder builder = new ProcessBuilder(command); Map<String, String> environment = builder.environment(); if (additionalEnv != null){ environment.putAll(additionalEnv); } StringBuilder outputBuilder; try { Process handle = builder.start(); InputStream stdErr = handle.getErrorStream(); InputStream stdOut = handle.getInputStream(); // TODO: we likely don't need this // OutputStream stdIn = handle.getOutputStream(); outputBuilder = new StringBuilder(); byte[] buff = new byte[4096]; // TODO: should we read both of these streams? // if we break out of the first loop is the process terminated? // read stderr for (int n; (n = stdErr.read(buff)) != -1;) { outputBuilder.append(new String(buff, 0, n)); } // read stdout for (int n; (n = stdOut.read(buff)) != -1;) { outputBuilder.append(new String(buff, 0, n)); } stdOut.close(); stdErr.close(); // TODO: stdin if we use it. // stdIn.close(); // the process should be closed by now? handle.waitFor(); handle.destroy(); exitValue = handle.exitValue(); // print the output from the command System.out.println(outputBuilder.toString()); System.out.println("Exit Value : " + exitValue); outputBuilder.append("Exit Value : " + exitValue); return outputBuilder.toString(); } catch (Exception e) { log.error("execute threw", e); exitValue = 5; return e.getMessage(); } } public static Double getBatteryLevel() { Platform platform = Platform.getLocalInstance(); try { if (platform.isWindows()) { // String ret = Runtime.execute("cmd.exe", "/C", "WMIC.exe", "PATH", "Win32_Battery", "Get", "EstimatedChargeRemaining"); String ret = Runtime.execute("WMIC.exe", "PATH", "Win32_Battery", "Get", "EstimatedChargeRemaining"); int pos0 = ret.indexOf("\n"); if (pos0 != -1) { pos0 = pos0 + 1; int pos1 = ret.indexOf("\n", pos0); String dble = ret.substring(pos0, pos1).trim(); Double r = Double.parseDouble(dble); return r; } } else if (platform.isLinux()) { String ret = Runtime.execute("acpitool"); int pos0 = ret.indexOf("Charging, "); if (pos0 != -1) { pos0 = pos0 + 10; int pos1 = ret.indexOf("%", pos0); String dble = ret.substring(pos0, pos1).trim(); Double r = Double.parseDouble(dble); return r; } log.info(ret); } else if (platform.isMac()) { String ret = Runtime.execute("pmset -g batt"); int pos0 = ret.indexOf("Battery-0"); if (pos0 != -1) { pos0 = pos0 + 10; int pos1 = ret.indexOf("%", pos0); String dble = ret.substring(pos0, pos1).trim(); Double r = Double.parseDouble(dble); return r; } log.info(ret); } } catch (Exception e) { log.info("execToString threw", e); } return null; } /** * This static method returns all the details of the class without it having * to be constructed. It has description, categories, dependencies, and peer * definitions. * * @return ServiceType - returns all the data * */ static public ServiceType getMetaData() { ServiceType meta = new ServiceType(Runtime.class.getCanonicalName()); meta.addDescription("Runtime singleton service responsible for the creation and registry of all other services"); meta.addCategory("framework"); meta.addPeer("cli", "Cli", "command line interpreter for the runtime"); return meta; } public ServiceData getServiceData() { return serviceData; } }