package org.genedb.crawl.hazelcast; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ConnectException; import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.MalformedURLException; import java.net.URL; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.Option; import com.hazelcast.core.Hazelcast; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.Instance; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; /* * A utility to startup independent Hazelcast instances, whose only purpose is to help separate crawl instances * by sharing the caching load. It starts up a lightweight http server, which will listen for status or stop * requests. * * It can be controlled from the command line using the crawl bash script. */ @SuppressWarnings("restriction") public class HazelcastService { public enum HazelcastServiceCommand { start, stop, status; } private static Logger logger = Logger.getLogger(HazelcastService.class); @Option(name = "-h", aliases = { "--help" }, usage = "Print help") public boolean help; @Option(name = "-x", aliases = { "--hazelcast" }, usage = "A hazelcast xml file", required = true) public File hazelcastFile; @Option(name = "-p", aliases = { "--port" }, usage = "The port to listen for commands on", required = false) public int port = 9999; @Option(name = "-c", aliases = { "--context" }, usage = "The server base context path (default is '/hazelcast/'), don't forget the slashes!", required = false) public String baseContext = "/hazelcast/"; @Option(name = "-t", aliases = { "--test" }, usage = "Add some hard-coded test values to this instance", required = false) public boolean testValues = false; @Option(name = "-l", aliases = { "--log" }, usage = "The path to a log4j property file.", required = false) public File logPath; @Argument(index = 0, usage = "Can be either start|status|stop.", required = true) public HazelcastServiceCommand command; private final String host = "localhost"; private HazelcastInstance hz; private HazelcastMonitor monitor; private HttpServer server; private void run() throws IOException { switch (command) { case start: this.start(); break; case stop: case status: request(url(command)); break; } } private void request(URL url) throws IOException { HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setUseCaches(false); try { int code = connection.getResponseCode(); System.out.println("Response Code: " + code); BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); String inputLine; while ((inputLine = in.readLine()) != null) System.out.println(inputLine); in.close(); } catch (ConnectException ce) { System.out.println("Connection refused. Service not running on port " + port); } finally { connection.disconnect(); } } private void start() throws IOException { if (logPath != null) { PropertyConfigurator.configure(logPath.getAbsolutePath()); } if (!hazelcastFile.isFile()) { throw new RuntimeException("Could not find hazelcast configuration file : " + hazelcastFile); } /* * Start a lightweight http server, make sure it can only be accessed * from the machine it's running on (don't want to allow remote * shutdown). */ server = HttpServer.create(new InetSocketAddress(host, port), port); server.createContext(baseContext + HazelcastServiceCommand.stop, new ShutdownHandler()); server.createContext(baseContext + HazelcastServiceCommand.status, new StatusHandler()); server.setExecutor(null); server.start(); // Config config = new Config(); // config.setConfigurationFile(hazelcastFile); // config.setConfigurationUrl(hazelcastFile.toURI().toURL()); // hz = Hazelcast.newHazelcastInstance(config); /* * Creating a Config object (as commented out above) and setting the * file using setConfigurationFile doesn't seem to work at all. The * system property approach does. */ String path = hazelcastFile.getAbsolutePath(); logger.info("Setting configuration file " + path); System.setProperty("hazelcast.config", path); hz = Hazelcast.getDefaultInstance(); logger.info(hz.getConfig().getConfigurationFile()); logger.info(hz.getConfig().getGroupConfig().getName()); monitor = new HazelcastMonitor(); hz.addInstanceListener(monitor); if (testValues) { hz.getMap("tst").put("1", "1"); hz.getMap("tst").put("2", "2"); hz.getMap("tst").put("3", "3"); logger.info(hz.getMap("tst").getId()); logger.info(hz.getMap("tst").size()); } System.out.println("-- HAZELCAST SERVICE WRAPPER READY --"); System.out.println(String.format("To check status, request: '%s'.", url(HazelcastServiceCommand.status))); System.out.println(String.format("To shut it down, request: '%s'.", url(HazelcastServiceCommand.stop))); } private URL url(HazelcastServiceCommand command) throws MalformedURLException { return new URL(String.format("http://%s:%s%s%s", host, port, baseContext, command)); } private class StatusHandler implements HttpHandler { public void handle(HttpExchange t) throws IOException { StringBuffer buffer = new StringBuffer(); buffer.append("This is the info : \n"); for (Instance instance : hz.getInstances()) { buffer.append(HazelcastMonitor.stats(HazelcastMonitor.getId(instance)) + "\n"); } String response = buffer.toString(); t.sendResponseHeaders(200, response.length()); OutputStream os = t.getResponseBody(); os.write(response.getBytes()); os.close(); } } private class ShutdownHandler implements HttpHandler { public void handle(HttpExchange t) throws IOException { Hazelcast.shutdownAll(); String response = "Shutting down"; t.sendResponseHeaders(200, response.length()); OutputStream os = t.getResponseBody(); os.write(response.getBytes()); os.close(); System.exit(-1); } } public static void main(String[] args) throws IOException { HazelcastService serviceWrapper = new HazelcastService(); CmdLineParser parser = new CmdLineParser(serviceWrapper); try { parser.parseArgument(args); if (serviceWrapper.help) { parser.setUsageWidth(80); parser.printUsage(System.out); System.exit(1); } serviceWrapper.run(); } catch (CmdLineException e) { System.out.println(e.getMessage()); parser.setUsageWidth(80); parser.printUsage(System.out); System.exit(1); } } }