/* * gw2live - GuildWars 2 Dynamic Map * * Website: http://gw2map.com * * Copyright 2013 zyclonite networx * http://zyclonite.net * Developer: Lukas Prettenthaler */ package net.zyclonite.gw2live; import java.util.Collection; import java.util.Map; import java.util.concurrent.CountDownLatch; import net.zyclonite.gw2live.handler.AdminRestHandler; import net.zyclonite.gw2live.handler.PostHandler; import net.zyclonite.gw2live.handler.RestHandler; import net.zyclonite.gw2live.handler.SockJsHandler; import net.zyclonite.gw2live.handler.StatRestHandler; import net.zyclonite.gw2live.listener.ChatListener; import net.zyclonite.gw2live.listener.ClusterListener; import net.zyclonite.gw2live.listener.GuildStatisticUpdateListener; import net.zyclonite.gw2live.listener.PlayerLocationListener; import net.zyclonite.gw2live.listener.StatisticUpdateListener; import net.zyclonite.gw2live.model.Subscriber; import net.zyclonite.gw2live.service.HazelcastCache; import net.zyclonite.gw2live.service.VertX; import net.zyclonite.gw2live.timer.BootstrapTimer; import net.zyclonite.gw2live.timer.LessFrequentTimer; import net.zyclonite.gw2live.timer.LiveEventTimer; import net.zyclonite.gw2live.util.AppConfig; import net.zyclonite.gw2live.util.EplUpdateListener; import net.zyclonite.gw2live.util.LocalCache; import net.zyclonite.gw2live.util.StaticDataLoader; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.log4j.xml.DOMConfigurator; import org.apache.logging.julbridge.JULLog4jBridge; import org.vertx.java.core.Handler; import org.vertx.java.core.buffer.Buffer; import org.vertx.java.core.eventbus.EventBus; import org.vertx.java.core.eventbus.Message; import org.vertx.java.core.json.JsonObject; /** * * @author zyclonite */ public class Application { private static final Log LOG = LogFactory.getLog(Application.class); private final CountDownLatch stopLatch = new CountDownLatch(1); private final VertX vertx; private final HazelcastCache hazelcast; private final AppConfig config; private final Object sync = new Object(); public Application() { config = AppConfig.getInstance(); vertx = VertX.getInstance(); hazelcast = HazelcastCache.getInstance(); hazelcast.getCluster().addMembershipListener(new ClusterListener()); hazelcast.getChatTopic().addMessageListener(new ChatListener()); hazelcast.getPlayerLocationTopic().addMessageListener(new PlayerLocationListener()); LocalCache.PVE_ENABLED = config.getBoolean("application.pve-enabled", false); LocalCache.WVW_ENABLED = config.getBoolean("application.wvw-enabled", false); loadStaticData(); bootstrapApplication(); initHandlers(); initStatements(); initWvwGuildStatements(); } private void loadStaticData() { final StaticDataLoader loader = new StaticDataLoader(); loader.loadData(); LOG.debug("Static data loaded"); } private void bootstrapApplication() { vertx.setTimer(1000, new BootstrapTimer()); } private void initHandlers() { vertx.registerPostHandler("/rest/:endpoint", new PostHandler()); vertx.registerGetHandler("/rest/:endpoint", new RestHandler()); vertx.registerGetHandler("/rest/:endpoint/lang/:lang", new RestHandler()); vertx.registerGetHandler("/rest/:endpoint/world/:world", new RestHandler()); vertx.registerGetHandler("/rest/:endpoint/world/:world/map/:map", new RestHandler()); vertx.registerGetHandler("/rest/:endpoint/match/:match", new RestHandler()); vertx.registerGetHandler("/rest/:endpoint/match/:match/map/:map", new RestHandler()); vertx.registerGetHandler("/rest/:endpoint/guildid/:guildid", new RestHandler()); vertx.registerGetHandler("/rest/:endpoint/guildname/:guildname", new RestHandler()); vertx.registerGetHandler("/rest/:endpoint/channel/:channel", new RestHandler()); vertx.registerGetHandler("/rest/:endpoint/event/:event", new RestHandler()); vertx.registerGetHandler("/rest/:endpoint/map/:map", new RestHandler()); vertx.registerGetHandler("/stats/:endpoint", new StatRestHandler()); vertx.registerGetHandler("/stats/:endpoint/:id", new StatRestHandler()); vertx.registerGetHandler("/stats/:endpoint/:id/:date", new StatRestHandler()); vertx.registerGetHandler("/stats/:endpoint/:id/:date/:limit", new StatRestHandler()); vertx.registerGetHandler("/admin/:endpoint", new AdminRestHandler()); vertx.registerSockJsHandler("/stream", new SockJsHandler()); } private void initStatements() { final Object obj = config.getProperty("statistics.statements.statement.name"); if (obj instanceof Collection) { final int size = ((Collection) obj).size(); for (int i = 0; i < size; i++) { final String name = config.getString("statistics.statements.statement(" + i + ").name"); final String epl = config.getString("statistics.statements.statement(" + i + ").epl"); final String[] output = config.getString("statistics.statements.statement(" + i + ").output").split(","); addStatement(name, epl, output); } LOG.debug("loaded " + size + " statements"); } else if (obj instanceof String) { final String name = config.getString("statistics.statements.statement.name"); final String epl = config.getString("statistics.statements.statement.epl"); final String[] output = config.getString("statistics.statements.statement.output").split(","); addStatement(name, epl, output); LOG.debug("loaded one statement"); } else { LOG.debug("no statements configured"); } } private void initWvwGuildStatements() { if (config.getBoolean("application.guildstats-enabled", false)) { final GuildStatisticUpdateListener statement = new GuildStatisticUpdateListener(); LocalCache.STATEMENTS.add(statement); } } private void addStatement(final String name, final String epl, final String[] output) { final StatisticUpdateListener statement = new StatisticUpdateListener(name, output, epl); LocalCache.STATEMENTS.add(statement); } private void sendAllConnectedUsers(final String message) { final JsonObject response = new JsonObject(); final JsonObject json = new JsonObject(); json.putString("message", message); response.putString("type", "broadcast"); response.putObject("data", json); final EventBus eb = vertx.getEventBus(); for (final Subscriber subscriber : LocalCache.SUBSCRIBER) { if (hazelcast.getNodeId().equals(subscriber.getNodeId())) { eb.publish(subscriber.getConnection(), new Buffer().appendString(response.encode())); } } } /** * @param args the command line arguments */ public static void main(String[] args) { JULLog4jBridge.assimilate(); DOMConfigurator.configureAndWatch(System.getProperty("user.dir") + "/log4j.xml", 60000); final Application main = new Application(); LOG.info("Application started"); main.addShutdownHook(); main.block(); } private void block() { while (true) { try { stopLatch.await(); break; } catch (InterruptedException e) { //Ignore } } } private void unblock() { stopLatch.countDown(); } public static synchronized void switchSlave() { for (final Long timer : LocalCache.TIMERS) { VertX.getInstance().cancelTimer(timer); } LocalCache.TIMERS.clear(); for (final EplUpdateListener statement : LocalCache.STATEMENTS) { statement.stop(); } LOG.debug("Timers cleared"); } public static synchronized void switchMaster() { final AppConfig config = AppConfig.getInstance(); LocalCache.TIMERS.add(VertX.getInstance().setPeriodic(config.getInt("application.timers.liveupdates", 10) * 1000, new LiveEventTimer())); LocalCache.TIMERS.add(VertX.getInstance().setPeriodic(config.getInt("application.timers.contentupdates", 30) * 1000, new LessFrequentTimer())); for (final EplUpdateListener statement : LocalCache.STATEMENTS) { statement.start(); } LOG.debug("Timers started"); } private void addShutdownHook() { Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { LOG.info("Application shutting down..."); sendAllConnectedUsers("Server is going down for an update, please try to reconnect in some minutes"); LOG.info("sent shutdown broadcast to all users"); synchronized (sync) { for (final Map.Entry<Long, Handler<Message<Buffer>>> entry : LocalCache.PVE_EVENT_LISTENERS.entrySet()) { vertx.getEventBus().unregisterHandler(LocalCache.EVENTS_PVE_PREFIX + entry.getKey(), entry.getValue()); } LocalCache.PVE_EVENT_LISTENERS.clear(); for (final Map.Entry<String, Handler<Message<Buffer>>> entry : LocalCache.WVW_EVENT_LISTENERS.entrySet()) { vertx.getEventBus().unregisterHandler(LocalCache.EVENTS_WVW_PREFIX + entry.getKey(), entry.getValue()); } LocalCache.WVW_EVENT_LISTENERS.clear(); switchSlave(); } vertx.shutdown(); } }); } }