package aQute.jpm.platform; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Node; import aQute.jpm.lib.CommandData; import aQute.jpm.lib.JVM; import aQute.jpm.lib.ServiceData; import aQute.lib.io.IO; class MacOS extends Unix { private final static Logger logger = LoggerFactory.getLogger(MacOS.class); static DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); static XPathFactory xpf = XPathFactory.newInstance(); @Override public File getGlobal() { return new File("/Library/Java/PackageManager").getAbsoluteFile(); } @Override public File getGlobalBinDir() { return new File("/usr/local/bin").getAbsoluteFile(); } @Override public File getLocal() { return IO.getFile("~/Library/PackageManager").getAbsoluteFile(); } @Override public void shell(String initial) throws Exception { run("open -n /Applications/Utilities/Terminal.app"); } @Override public String getName() { return "MacOS"; } @Override public String createCommand(CommandData data, Map<String,String> map, boolean force, String... extra) throws Exception { if (data.bin == null) data.bin = getExecutable(data); File f = new File(data.bin); if (f.isDirectory()) { f = new File(data.bin, data.name); data.bin = f.getAbsolutePath(); } if (!force && f.exists()) return "Command already exists " + data.bin; process("macos/command.sh", data, data.bin, map, extra); return null; } @Override public String createService(ServiceData data, Map<String,String> map, boolean force, String... extra) throws Exception { // File initd = getInitd(data); File launch = getLaunch(data); if (!force && launch.exists()) return "Cannot create service " + data.name + " because it exists"; process("macos/launch.sh", data, launch.getAbsolutePath(), map, add(extra, data.serviceLib)); return null; } @Override public String deleteService(ServiceData data) { // File initd = getInitd(data); File launch = getLaunch(data); if (launch.exists() && !launch.delete()) return "Cannot delete service " + data.name + " because it exists and cannot be deleted: " + launch; return null; } @Override public void installDaemon(boolean user) throws IOException { String dest = "~/Library/LaunchAgents/org.jpm4j.run.plist"; if (!user) { dest = "/Library/LaunchAgents/org.jpm4j.run.plist"; } IO.copy(getClass().getResource("macos/daemon.plist"), IO.getFile(dest)); } @Override public void uninstallDaemon(boolean user) throws IOException { if (user) IO.delete(new File("~/Library/LaunchAgents/org.jpm4j.run.plist")); else IO.delete(new File("/Library/LaunchAgents/org.jpm4j.run.plist")); } @Override public void uninstall() throws IOException {} public String defaultCacertsPassword() { return "changeit"; } public String toString() { return "MacOS/Darwin"; } /** * Return the VMs on the platform. * * @throws Exception */ @Override public void getVMs(Collection<JVM> vms) throws Exception { String paths[] = { "/System/Library/Java/JavaVirtualMachines", "/Library/Java/JavaVirtualMachines" }; for (String path : paths) { for (File vmdir : new File(path).listFiles()) { JVM jvm = getJVM(vmdir); if (jvm != null) vms.add(jvm); } } } @Override public JVM getJVM(File vmdir) throws Exception { if (!vmdir.isDirectory()) { return null; } File contents = new File(vmdir, "Contents"); if (!contents.isDirectory()) { logger.debug("Found a directory {}, but it does not have the expected Contents directory", vmdir); return null; } File plist = new File(contents, "Info.plist"); if (!plist.isFile()) { logger.debug("The VM in {} has no Info.plist with the necessary details", vmdir); return null; } File home = new File(contents, "Home"); String error = verifyVM(home); if (error != null) { reporter.error("Invalid vm directory for MacOS %s: %s", vmdir, error); return null; } DocumentBuilder db = dbf.newDocumentBuilder(); try { Document doc = db.parse(plist); XPath xp = xpf.newXPath(); Node versionNode = (Node) xp.evaluate("//dict/key[text()='JVMVersion']", doc, XPathConstants.NODE); Node platformVersionNode = (Node) xp.evaluate("//dict/key[text()='JVMPlatformVersion']", doc, XPathConstants.NODE); Node vendorNode = (Node) xp.evaluate("//dict/key[text()='JVMVendor']", doc, XPathConstants.NODE); Node capabilitiesNode = (Node) xp.evaluate("//dict/key[text()='JVMCapabilities']", doc, XPathConstants.NODE); JVM jvm = new JVM(); jvm.name = vmdir.getName(); jvm.path = home.getCanonicalPath(); jvm.platformRoot = vmdir.getCanonicalPath(); jvm.version = getSiblingValue(versionNode); jvm.platformVersion = getSiblingValue(platformVersionNode); jvm.vendor = getSiblingValue(vendorNode); return jvm; } catch (Exception e) { logger.debug("Could not parse the Info.plist in {}", vmdir, e); throw e; } } private String getSiblingValue(Node node) { if (node == null) return null; node = node.getNextSibling(); while (node.getNodeType() == Node.TEXT_NODE) node = node.getNextSibling(); return node.getTextContent(); } }