package cloudone.internal; import cloudone.ApplicationInfo; import cloudone.C1Application; import cloudone.RuntimeInfo; import cloudone.ServiceFullName; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.StringTokenizer; import java.util.jar.Manifest; /** * @author Martin Mares (martin.mares at oracle.com) */ public class RuntimeInfoImpl implements RuntimeInfo { public static class Builder { private static final String MANIFEST_SERVICE_NAME = "X-CloudOne-ServiceName"; private static final String MANIFEST_APPLICATION_NAMES = "X-CloudOne-Applications"; private final RuntimeInfoImpl instance = new RuntimeInfoImpl(); private Manifest manifest; public Builder addApplication(C1Application c1Application) { instance.applicationInfos.add(new ApplicationInfoImpl(c1Application)); return this; } private Options createInternalOptions() { Options result = new Options(); result.addOption("h", "help", false, "Prints this help information"); result.addOption(null, "group", true, "Group id"); result.addOption(null, "artifact", true, "Artifact id"); result.addOption("v", "version", true, "Version"); result.addOption(null, "c1home", true, "Cloud one home directory"); result.addOption(null, "adminPort", true, "Administration port of this service instance"); return result; } public Builder processCommandLineArgs(String[] args) throws ParseException { Options opts = createInternalOptions(); for (ApplicationInfo appInfo : instance.applicationInfos) { Options op = appInfo.getApplication().getOptions(); if (op != null) { for (Option option : op.getOptions()) { opts.addOption(option); } } opts.addOption(null, appInfo.getName() + ".port", true, "Port for " + appInfo.getName()); } if (instance.applicationInfos.size() == 1) { opts.addOption("p", "port", true, "Port for the application"); } instance.cmdlOptions = opts; //Parse cmd line args DefaultParser parser = new DefaultParser(); instance.commandLine = parser.parse(opts, args); return this; } /** * Find application definition in MANIFES-MF */ public Builder findApplications() throws Exception { String value = getPreferredManifest().getMainAttributes().getValue(MANIFEST_APPLICATION_NAMES); if (value == null || value.length() == 0) { throw new Exception("No application is configured. Please provide " + MANIFEST_APPLICATION_NAMES + " attribute in the MANIFEST.MF file."); } StringTokenizer stok = new StringTokenizer(value, ",;:"); while (stok.hasMoreTokens()) { String className = stok.nextToken(); Class clazz = null; try { clazz = Class.forName(className); } catch (ClassNotFoundException e) { throw new Exception("Can not find class for application defined in MANIFEST.MF: " + className); } if (!C1Application.class.isAssignableFrom(clazz)) { throw new Exception("Application " + className + " is not C1Application!"); } try { addApplication((C1Application) clazz.newInstance()); } catch (InstantiationException | IllegalAccessException e) { throw new Exception("Application " + className + " does not provides public empty construtor!"); } } return this; } private Manifest getPreferredManifest() { if (manifest == null) { Enumeration<URL> resources = null; try { resources = getClass().getClassLoader().getResources("META-INF/MANIFEST.MF"); } catch (IOException e) { throw new RuntimeException("Can not access META-INF/MANIFEST.MF in using ClassLoader."); } while (resources.hasMoreElements()) { URL url = resources.nextElement(); try { Manifest mnf = new Manifest(url.openStream()); if (mnf.getMainAttributes().getValue(MANIFEST_SERVICE_NAME) != null) { if (this.manifest == null) { this.manifest = mnf; } else { throw new RuntimeException("More then one manifest file with cloud one servie attribute."); } } } catch (IOException E) { throw new RuntimeException("Can not read/parse manifest file from " + url); } } } if (manifest == null) { throw new RuntimeException("Can not find manifest file with cloud one service related attributes."); } return manifest; } public void build() throws Exception { instance.applicationInfos = Collections.unmodifiableList(instance.applicationInfos); //Find C1_HOME if (instance.commandLine.getOptionValue("c1home") != null) { instance.homeDirectory = new File(instance.commandLine.getOptionValue("c1home")); } else if (System.getenv("C1_HOME") != null) { instance.homeDirectory = new File(System.getenv("C1_HOME")); } else { instance.homeDirectory = new File("/var/cloud1"); } if (!instance.homeDirectory.exists()) { if (!instance.homeDirectory.mkdirs()) { throw new RuntimeException("C1_HOME directory (" + instance.homeDirectory.getPath() + ") does not exists and cannot be created! " + "Plese provide this directory or set C1_HOME environmnet variable or c1home parametter to the existing and accessible directory"); } } if (!instance.homeDirectory.isDirectory()) { throw new RuntimeException("C1_HOME (" + instance.homeDirectory.getPath() + ") is not directory! " + "Plese provide this directory or set C1_HOME environmnet variable or c1home parametter to the existing and accessible directory"); } //Application ports int defaultPort = -1; try { String val = instance.commandLine.getOptionValue('p'); if (val != null) { defaultPort = Integer.parseInt(val); } } catch (Exception exc) { throw new Exception("Cannot parse -p / --port parameter value."); } for (ApplicationInfo appInfo : instance.applicationInfos) { ((ApplicationInfoImpl) appInfo).setPort(defaultPort); try { String val = instance.commandLine.getOptionValue('p'); if (val != null) { ((ApplicationInfoImpl) appInfo).setPort(Integer.parseInt(val)); } } catch (Exception exc) { throw new Exception("Cannot parse --" + appInfo.getName() + ".port parameter value."); } } //Admin port try { String val = instance.commandLine.getOptionValue("adminPort"); if (val != null) { instance.adminPort = Integer.parseInt(val); } } catch (Exception exc) { throw new Exception("Cannot parse --adminPort parameter value."); } //Service full name String serviceName = getPreferredManifest().getMainAttributes().getValue(MANIFEST_SERVICE_NAME); if (serviceName == null) { throw new RuntimeException("Can not find " + MANIFEST_SERVICE_NAME + " attribute in MANIFEST.MF file"); } ServiceFullName fullName = new ServiceFullName(serviceName); if (instance.getCommandLine().getOptionValue("group") != null) { fullName = new ServiceFullName(instance.getCommandLine().getOptionValue("group"), fullName.getArtifactId(), fullName.getVersion()); } if (instance.getCommandLine().getOptionValue("artifact") != null) { fullName = new ServiceFullName(fullName.getGroupId(), instance.getCommandLine().getOptionValue("artifact"), fullName.getVersion()); } if (instance.getCommandLine().getOptionValue('v') != null) { fullName = new ServiceFullName(fullName.getGroupId(), fullName.getArtifactId(), instance.getCommandLine().getOptionValue('v')); } instance.serviceFullName = fullName; //Build RuntimeInfoImpl.instance = this.instance; } } private static RuntimeInfoImpl instance; private List<ApplicationInfo> applicationInfos = new ArrayList<>(); private int adminPort = -1; private Options cmdlOptions; private CommandLine commandLine; private File homeDirectory; private ServiceFullName serviceFullName; private final long createdTimestamp = System.currentTimeMillis(); @Override public List<ApplicationInfo> getApplicationInfos() { return Collections.unmodifiableList(applicationInfos); } @Override public int getAdminPort() { return adminPort; } public void setAdminPort(int adminPort) { this.adminPort = adminPort; } @Override public Options getCmdlOptions() { return cmdlOptions; } @Override public CommandLine getCommandLine() { return commandLine; } public static RuntimeInfoImpl getInstance() { return instance; } @Override public File getHomeDirectory() { return homeDirectory; } @Override public long getCreatedTimestamp() { return createdTimestamp; } @Override public ServiceFullName getServiceFullName() { return serviceFullName; } }