package org.myrobotlab.framework.repo; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import org.myrobotlab.codec.CodecUtils; import org.myrobotlab.framework.ServiceType; import org.myrobotlab.io.FileIO; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.logging.Logging; import org.myrobotlab.logging.LoggingFactory; import org.slf4j.Logger; /** * ServiceData class contains all of the Services meta data. This includes : 1. * Dependency information - what libraries are needed to run the class 2. * Categories of the service 3. Peers of the service * * All this information is Service "type" related - non of it is instance * specific. ServiceData has to be created during "build" time since most of the * services contain dependencies which might not be fulfilled during runtime. * * ServiceData.generate() creates serviceData.json which is packaged by the * build in : /resource/framework/serviceData.json * * When MyRobotLab runs for the first time, it will extract this file into the * .myrobotlab directory. * * @author GroG * */ public class ServiceData implements Serializable { private static final long serialVersionUID = 1L; transient public final static Logger log = LoggerFactory.getLogger(ServiceData.class); /** * all services meta data is contained here */ TreeMap<String, ServiceType> serviceTypes = new TreeMap<String, ServiceType>(); /** * the set of all categories */ TreeMap<String, Category> categoryTypes = new TreeMap<String, Category>(); static private ServiceData localInstance = null; static private String serviceDataCacheFileName = String.format("%s%sserviceData.json", FileIO.getCfgDir(), File.separator); static public ServiceData getLocalInstance() { if (localInstance == null) { // step 1 - try local file in the .myrobotlab directory // step 2 - extract the file from the jar // WE CAN NOT GENERATE THIS FILE DURING RUNTIME !!! // step 3 - if 1 & 2 fail - then we can 'assume' were in develop // time (we'll isJar check and error if not) // - generate it and put it in // getRoot()/resource/framework/serviceData.json File jsonFile = new File(serviceDataCacheFileName); try { log.info("try #1 loading local file {}", jsonFile); String data = FileIO.toString(jsonFile); if (data == null || data.length() == 0) { throw new IOException("service data file [{}] contains no data"); } localInstance = CodecUtils.fromJson(data, ServiceData.class); return localInstance; } catch (FileNotFoundException fe) { try { log.info("could not find {}", serviceDataCacheFileName); jsonFile.getParentFile().mkdirs(); String extractFrom = "/resource/framework/serviceData.json"; log.info("try #2 {} not found - extracting from {}", jsonFile.getName(), extractFrom); FileIO.extract(extractFrom, jsonFile.getAbsolutePath()); String data = FileIO.toString(jsonFile); localInstance = CodecUtils.fromJson(data, ServiceData.class); } catch (Exception e) { log.info("could not extract from {}", "/resource/framework/serviceData.json"); String newJson = FileIO.gluePaths(FileIO.getRoot(), "/resource/framework/serviceData.json"); log.info("try #3 serviceData.json not found in resource ! - generating and putting it in {}", newJson); if (FileIO.isJar()) { log.error("we are in a jar! This is very bad!"); } else { log.info("we are not in a jar ... ok I guess we are doing a \"refresh\" on serviceData.json"); } try { ServiceData sd = ServiceData.generate(); String json = CodecUtils.toJson(sd); log.info("saving generated serviceData.json to {}", newJson); FileOutputStream fos = new FileOutputStream(newJson); fos.write(json.getBytes()); fos.close(); log.info("saved -- goodtimes"); localInstance = sd; } catch (Exception e2) { log.error("I've tried everything! .. I give up"); Logging.logError(e2); } } localInstance.save(); } catch (Exception e) { Logging.logError(e); } } return localInstance; } /** * This method has to check the environment first in order to tell if its * Develop-Time or Run-Time because the method of generating a service list is * different depending on current environment * * Develop-Time can simply filter and process the files on the file system * given by the code source location * * Run-Time must extract itself and scan/filter zip entries which is * potentially a lengthy process, and should only have to be done once for the * lifetime of the version or mrl * * * @return * @throws IOException */ static public ServiceData generate() throws IOException { log.info("================ generating serviceData.json begin ================"); ServiceData sd = new ServiceData(); // get services - all this could be done during Runtime // although running through zip entries would be a bit of a pain // epecially if you have to spin through 12 megs of data List<String> services = FileIO.getServiceList(); log.info("found {} services", services.size()); for (int i = 0; i < services.size(); ++i) { String fullClassName = services.get(i); // log.info("querying {}", fullClassName); try { Class<?> theClass = Class.forName(fullClassName); Method method = theClass.getMethod("getMetaData"); ServiceType serviceType = (ServiceType) method.invoke(null); if (!fullClassName.equals(serviceType.getName())) { log.error(String.format("Class name %s not equal to the ServiceType's name %s", fullClassName, serviceType.getName())); } sd.add(serviceType); for (String cat : serviceType.categories) { Category category = null; if (sd.categoryTypes.containsKey(cat)) { category = sd.categoryTypes.get(cat); } else { category = new Category(); category.name = category.name; } category.serviceTypes.add(serviceType.getName()); sd.categoryTypes.put(cat, category); } } catch (Exception e) { log.error(String.format("%s does not have a static getMetaData method", fullClassName)); } } log.info("================ generating serviceData.json end ================"); return sd; } public ServiceData() { } public void add(ServiceType serviceType) { serviceTypes.put(serviceType.getName(), serviceType); } public boolean containsServiceType(String fullServiceName) { return serviceTypes.containsKey(fullServiceName); } public List<ServiceType> getAvailableServiceTypes() { ArrayList<ServiceType> ret = new ArrayList<ServiceType>(); for (Map.Entry<String, ServiceType> o : serviceTypes.entrySet()) { if (o.getValue().isAvailable()) { ret.add(o.getValue()); } } return ret; } public Category getCategory(String filter) { if (filter == null) { return null; } if (categoryTypes.containsKey(filter)) { return categoryTypes.get(filter); } return null; } public String[] getCategoryNames() { String[] cat = new String[categoryTypes.size()]; int i = 0; for (Map.Entry<String, Category> o : categoryTypes.entrySet()) { cat[i] = o.getKey(); ++i; } return cat; } public HashSet<String> getServiceTypeDependencyKeys() { HashSet<String> uniqueKeys = new HashSet<String>(); for (Map.Entry<String, ServiceType> o : serviceTypes.entrySet()) { ServiceType st = o.getValue(); if (st.dependencies != null) { for (String org : st.dependencies) { uniqueKeys.add(org); } } } return uniqueKeys; } public String[] getServiceTypeNames() { return getServiceTypeNames(null); } public String[] getServiceTypeNames(String categoryFilterName) { if (categoryFilterName == null || categoryFilterName.length() == 0 || categoryFilterName.equals("all")) { String[] ret = serviceTypes.keySet().toArray(new String[0]); Arrays.sort(ret); return ret; } if (!categoryTypes.containsKey(categoryFilterName)) { return new String[] {}; } Category cat = categoryTypes.get(categoryFilterName); return cat.serviceTypes.toArray(new String[cat.serviceTypes.size()]); } public ServiceType getServiceType(String fullTypeName) { return serviceTypes.get(fullTypeName); } public ArrayList<ServiceType> getServiceTypes() { ArrayList<ServiceType> ret = new ArrayList<ServiceType>(); for (Map.Entry<String, ServiceType> o : serviceTypes.entrySet()) { ret.add(o.getValue()); } return ret; } public boolean save() { log.info("saving {}", serviceDataCacheFileName); return save(serviceDataCacheFileName); } public boolean save(String filename) { try { FileOutputStream fos = new FileOutputStream(filename); String json = CodecUtils.toJson(this); fos.write(json.getBytes()); fos.close(); return true; } catch (Exception e) { Logging.logError(e); } return false; } // TWO LEVELS !!! 1. Run-time checking & Build-time checking // Built-time checking // build time has access to the repo - can cross check dependencies to make // sure they are in the library // // Runtime checking // for all Peers - do ALL THERE TYPES CURRENTLY EXIST ??? // FIXME - TODO - FIND public ArrayList<Category> getCategories() { ArrayList<Category> categories = new ArrayList<Category>(); for (Category category : categoryTypes.values()) { categories.add(category); } return categories; } static public Set<String> getDependencyKeys(String fullTypeName) { HashSet<String> keys = new HashSet<String>(); ServiceData sd = getLocalInstance(); if (!sd.serviceTypes.containsKey(fullTypeName)) { log.error("{} not defined in service types"); return keys; } ServiceType st = localInstance.serviceTypes.get(fullTypeName); return st.getDependencies(); } public static void main(String[] args) { try { LoggingFactory.init(); // LoggingFactory.getInstance().setLevel("INFO"); // LoggingFactory.getInstance().addAppender(Appender.FILE); String path = ""; if (args.length > 0) { path = args[0]; } String filename = FileIO.gluePaths(path, "serviceData.json"); log.info("generating {}", filename); if (path.length() > 0) { new File(path).mkdirs(); } // THIS IS FOR ANT BUILD - DO NOT CHANGE !!! - BEGIN ---- ServiceData sd = generate(); FileOutputStream fos = new FileOutputStream(filename); fos.write(CodecUtils.toJson(sd).getBytes()); fos.close(); // THIS IS FOR ANT BUILD - DO NOT CHANGE !!! - END ---- } catch (Exception e) { Logging.logError(e); } } }