com.dawg6.web.dhcalc.server.DHCalcServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.dawg6.web.dhcalc.server.DHCalcServiceImpl.java

Source

/*******************************************************************************
 * Copyright (c) 2014, 2015 Scott Clarke (scott@dawg6.com).
 *
 * This file is part of Dawg6's Demon Hunter DPS Calculator.
 *
 * Dawg6's Demon Hunter DPS Calculator is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Dawg6's Demon Hunter DPS Calculator is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
package com.dawg6.web.dhcalc.server;

import java.io.IOException;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.dawg6.d3api.shared.ApiData;
import com.dawg6.d3api.shared.CareerProfile;
import com.dawg6.d3api.shared.Const;
import com.dawg6.d3api.shared.Hero;
import com.dawg6.d3api.shared.HeroProfile;
import com.dawg6.d3api.shared.ItemInformation;
import com.dawg6.d3api.shared.Leaderboard;
import com.dawg6.d3api.shared.Realm;
import com.dawg6.d3api.shared.SeasonIndex;
import com.dawg6.gwt.server.util.ThreadPool;
import com.dawg6.web.dhcalc.client.DHCalcService;
import com.dawg6.web.dhcalc.server.db.couchdb.AccountDocument;
import com.dawg6.web.dhcalc.server.db.couchdb.CouchDBDHCalcDatabase;
import com.dawg6.web.dhcalc.server.db.couchdb.CouchDBDHCalcParameters;
import com.dawg6.web.dhcalc.server.db.couchdb.NewsDocument;
import com.dawg6.web.dhcalc.shared.calculator.ActiveSkill;
import com.dawg6.web.dhcalc.shared.calculator.Build;
import com.dawg6.web.dhcalc.shared.calculator.CharacterData;
import com.dawg6.web.dhcalc.shared.calculator.Damage;
import com.dawg6.web.dhcalc.shared.calculator.DamageResult;
import com.dawg6.web.dhcalc.shared.calculator.ExportData;
import com.dawg6.web.dhcalc.shared.calculator.FiringData;
import com.dawg6.web.dhcalc.shared.calculator.FormData;
import com.dawg6.web.dhcalc.shared.calculator.NewsItem;
import com.dawg6.web.dhcalc.shared.calculator.ProfileHelper;
import com.dawg6.web.dhcalc.shared.calculator.Rune;
import com.dawg6.web.dhcalc.shared.calculator.Util;
import com.dawg6.web.dhcalc.shared.calculator.Version;
import com.dawg6.web.dhcalc.shared.calculator.stats.DBStatistics;
import com.dawg6.web.dhcalc.shared.calculator.stats.DpsTableEntry;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * The server-side implementation of the RPC service.
 */
@SuppressWarnings("serial")
public class DHCalcServiceImpl extends RemoteServiceServlet implements DHCalcService {

    private final static Object lock = new Object();
    private final static ThreadPool pool = new ThreadPool(20);

    private static final Logger log = Logger.getLogger(DHCalcServiceImpl.class.getName());

    public static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,###.####");

    {
        Util.getInstance().setFormatter(new Util.Formatter() {

            @Override
            public String format(double d) {
                return DECIMAL_FORMAT.format(d);
            }
        });
    }

    private final Gson gson = new Gson();

    @Override
    public CareerProfile getProfile(final Realm realm, String profile, final int tag) {

        try {
            profile = URLEncoder.encode(profile, "UTF-8");

            log.info("getProfile(" + realm.getDisplayName() + "," + profile + "-" + tag + ")");
            CareerProfile career = IO.getInstance().readCareerProfile(realm, profile, tag);

            //         log.info("Career: " + gson.toJson(career));

            if (career == null) {
                career = new CareerProfile();
                career.code = "Timeout";
                career.reason = "Timeout";

            }
            if (career.code != null)
                log.info(realm.getDisplayName() + "/" + profile + "-" + tag + " Code: " + career.code + ", Reason: "
                        + career.reason);
            else {

                final CareerProfile c2 = career;
                final String p2 = profile;

                pool.add(new Runnable() {

                    @Override
                    public void run() {
                        scanHeroes(realm, p2, tag, c2);
                    }
                });

            }

            return career;

        } catch (Exception e) {
            log.log(Level.SEVERE, "Exception Getting Profile", e);
            return null;
        }
    }

    private static Set<AccountDocument> accountLock = new HashSet<AccountDocument>();

    private void scanHeroes(Realm realm, String profile, int tag, CareerProfile career) {

        AccountDocument a = new AccountDocument();
        a.setRealm(realm);
        a.setProfile(profile.toLowerCase());
        a.setTag(tag);
        long now = System.currentTimeMillis();
        long old = now - (1000L * 60L * 60L * 4L);

        try {
            synchronized (accountLock) {
                if (accountLock.contains(a)) {
                    return;
                } else {
                    accountLock.add(a);
                }
            }

            List<AccountDocument> accounts = CouchDBDHCalcDatabase.getInstance().findAll(AccountDocument.class);

            boolean changed = false;

            for (AccountDocument ad : accounts) {
                if (ad.equals(a)) {

                    if (ad.getLastChecked() >= old)
                        return;

                    a = ad;
                    break;
                }
            }

            changed = true;
            a.setLastChecked(now);

            if (a.getHeroes() == null)
                a.setHeroes(new TreeSet<Integer>());

            if ((career.heroes != null) && (career.heroes.length > 0)) {
                Set<Integer> set = a.getHeroes();

                for (Hero h : career.heroes) {

                    if ((h != null) && (h.clazz != null) && (h.clazz.equals(Const.CLASS_DEMONHUNTER))) {
                        if (!set.contains(h.id)) {
                            set.add(h.id);
                            changed = true;
                        }

                        HeroProfile hero = getHero(realm, profile, tag, h.id, false);

                        if ((hero.code != null) && (hero.code.equals(Const.NOT_FOUND))) {
                            set.remove(h.id);
                            changed = true;
                        }
                    }
                }
            } else if (!a.getHeroes().isEmpty()) {
                a.setHeroes(new TreeSet<Integer>());
                changed = true;
            }

            if (changed) {
                if (a.getRevision() == null)
                    log.info("Creating account " + a);
                else
                    log.info("Updating account " + a);

                CouchDBDHCalcDatabase.getInstance().persist(a);
            }
        } finally {
            synchronized (accountLock) {
                accountLock.remove(a);
            }
        }
    }

    @Override
    public ItemInformation getItem(Realm realm, String item) {

        try {
            //         log.info("getItem(" + realm.getDisplayName() + "," + item + ")");

            ItemInformation result = IO.getInstance().readItemInformation(realm, item);

            //         log.info("Item: " + gson.toJson(result));

            if (result == null) {
                result = new ItemInformation();
                result.code = "Timeout";
                result.reason = "Timeout";

            }

            if (result.code != null)
                log.info(realm.getDisplayName() + "/" + item + " Code: " + result.code + ", Reason: "
                        + result.reason);

            return result;

        } catch (Exception e) {
            log.log(Level.SEVERE, "Exception Getting Profile", e);
            return null;
        }
    }

    @Override
    public HeroProfile getHero(Realm realm, String profile, int tag, int id) {
        return getHero(realm, profile, tag, id, true);
    }

    public HeroProfile getHero(final Realm realm, String profile, final int tag, int id, boolean updateAccount) {

        if (updateAccount) {

            final String p2 = profile;

            new Thread(new Runnable() {

                @Override
                public void run() {
                    getProfile(realm, p2, tag);
                }
            }).start();
        }

        try {
            profile = URLEncoder.encode(profile, "UTF-8");

            log.info("getHero(" + realm.getDisplayName() + "," + profile + "-" + tag + "/" + id + ")");

            HeroProfile hero = IO.getInstance().readHeroProfile(realm, profile, tag, id);

            if (hero == null) {
                hero = new HeroProfile();
                hero.code = "Timeout";
                hero.reason = "Timeout";

            }

            if (hero.code != null)
                log.warning(realm.getDisplayName() + "/" + profile + "-" + tag + " Code: " + hero.code
                        + ", Reason: " + hero.reason);

            if (hero.items != null) {

                Map<String, ItemInformation> items = new TreeMap<String, ItemInformation>();

                for (Map.Entry<String, ItemInformation> e : hero.items.entrySet()) {
                    ItemInformation item = getItemNoLog(realm, e.getValue().tooltipParams);

                    if (item != null) {
                        items.put(e.getKey(), item);
                    }

                    // if (item.gems != null) {
                    // for (int i = 0; i < item.gems.length; i++) {
                    // ItemInformation.Gem g = item.gems[i];
                    // g.item = getItem(server, g.item.tooltipParams);
                    // }
                    // }
                }

                hero.items = items;
            }

            return hero;

        } catch (Exception e) {
            log.log(Level.SEVERE, "Exception Getting Hero", e);
            return null;
        }
    }

    private ItemInformation getItemNoLog(Realm realm, String tooltipParams) throws JsonParseException, IOException {
        return IO.getInstance().readItemInformation(realm, tooltipParams);
    }

    public String serializeFormData(FormData data) {
        try {
            String key = String.valueOf(Math.random() + "." + Math.random() + ".FormData");
            ObjectMapper mapper = new ObjectMapper();

            String result = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(data);

            ClientBuffer.getInstance().put(key, result.getBytes());

            return key;

        } catch (Exception e) {
            log.log(Level.SEVERE, "Exception Serializing Form Data", e);
            return null;
        }
    }

    @Override
    public FormData getClientData(String client) {
        String key = client + ".FormData";

        Object value = ClientBuffer.getInstance().get(key);

        if (value == null) {

            try {
                Thread.sleep(1000);
            } catch (Exception ignore) {
            }

            return null;
        }

        try {
            return (FormData) value;
        } catch (Exception e) {
            log.log(Level.SEVERE, "Exception Deserializing Form Data", e);
            return null;
        }
    }

    @Override
    public String exportData(ExportData data) {
        ExportExcel excel = new ExportExcel(data);
        try {
            String key = String.valueOf(Math.random() + "." + Math.random() + ".Excel");
            byte[] bytes = excel.export();
            ClientBuffer.getInstance().put(key, bytes);

            return key;
        } catch (Exception e) {
            log.log(Level.SEVERE, "Exception Exporting Excel data", e);
            return null;
        }
    }

    @Override
    public Version getVersion() {
        return Version.getVersion();
    }

    public String toJson(ApiData object) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            String str = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);

            return str;
        } catch (Exception e) {
            log.log(Level.SEVERE, "Exception Serializing Object", e);
            return null;
        }
    }

    public ApiData fromJson(String json, String type) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            return (ApiData) mapper.readValue(json, Class.forName(type));
        } catch (Exception e) {
            log.log(Level.SEVERE, "Exception Deserializing JSON", e);
            return null;
        }
    }

    @Override
    public void logData(final CharacterData data) {

        new Thread(new Runnable() {

            @Override
            public void run() {
                data.setDefaults();

                log.info("Logging data for " + data.getProfile() + "-" + data.getTag() + "("
                        + data.getRealm().name() + ")/" + data.getHero());

                DpsTableEntry entry = calculateDps(data);

                CouchDBDHCalcDatabase.getInstance().logDps(entry);
            }
        }).start();

    }

    public DpsTableEntry calculateDps(CharacterData data) {

        Build build = new Build();
        build.setSkills(data.getSkills());

        DpsTableEntry entry = new DpsTableEntry();

        data.setDefaults();
        ProfileHelper.updateWeaponDamage(data);
        ProfileHelper.updateCdr(data);
        data.setDefaults();

        entry.setBattletag(data.getProfile() + "-" + data.getTag() + "/" + data.getHero());
        entry.setRealm(data.getRealm());
        entry.setBuild(build);
        entry.setParagon_dex(data.getParagonDexterity());
        entry.setParagon_cc(data.getParagonCC());
        entry.setParagon_cdr(data.getParagonCDR());
        entry.setParagon_chd(data.getParagonCHD());
        entry.setParagon_ias(data.getParagonIAS());
        entry.setParagon_hatred(data.getParagonHatred());
        entry.setParagon_rcr(data.getParagonRCR());
        entry.setParagon(data.getParagon());
        entry.setProfile(data.getProfile());
        entry.setTag(data.getTag());
        entry.setHeroId(data.getHero());
        entry.setHeroName(data.getHeroName());
        entry.setPlayerAps(data.getAps());
        entry.setSeasonal(data.isSeasonal());
        entry.setHardcore(data.isHardcore());
        entry.setDead(data.isDead());
        entry.setSheetDps(data.getSheetDps());
        entry.setLevel(data.getLevel());

        data.setNumAdditional(0);
        calculateDamage(data, entry);
        data.setNumAdditional(10);
        calculateDamage(data, entry);

        entry.setWhen(System.currentTimeMillis());

        return entry;
    }

    private void calculateDamage(CharacterData data, DpsTableEntry entry) {

        DamageResult damage = FiringData.calculateDamages(data);

        double total = 0.0;
        double totalElite = 0.0;
        double e = 1.0 + data.getTotalEliteDamage();

        for (Damage d : damage.damages) {
            total += d.actualDamage;
            // System.out.println(d.log);
        }

        total = total / damage.duration;

        totalElite = total * e;

        if (data.getNumAdditional() == 0) {
            entry.setSingle(total);
            entry.setSingle_elite(totalElite);
        } else {
            entry.setMultiple(total);
            entry.setMultiple_elite(totalElite);
        }
    }

    @Override
    public DBStatistics getStats(Rune sentryRune, ActiveSkill[] skills, Rune[] runes) {
        return CouchDBDHCalcDatabase.getInstance().getStatistics(sentryRune, skills, runes);
    }

    @Override
    public SeasonIndex getSeasonEraIndex(Realm realm) {
        try {
            SeasonIndex seasons = IO.getInstance().readSeasonIndex(realm);
            SeasonIndex eras = IO.getInstance().readEraIndex(realm);

            seasons.eras = eras.eras;
            seasons.current_era = eras.current_era;

            return seasons;

        } catch (RuntimeException e) {
            log.log(Level.SEVERE, "Exception", e);
            throw e;
        } catch (Exception e2) {
            log.log(Level.SEVERE, "Exception", e2);
            throw new RuntimeException(e2);
        }
    }

    @Override
    public Leaderboard getLeaderboard(Realm realm, int seasonEra, boolean isEra, String which) {
        try {
            if (isEra) {
                return IO.getInstance().readEraLeaderboard(realm, seasonEra, which);
            } else {
                return IO.getInstance().readSeasonLeaderboard(realm, seasonEra, which);
            }

        } catch (RuntimeException e) {
            log.log(Level.SEVERE, "Exception", e);
            throw e;
        } catch (Exception e2) {
            log.log(Level.SEVERE, "Exception", e2);
            throw new RuntimeException(e2);
        }
    }

    @Override
    public NewsItem[] getNews() {
        try {
            List<NewsDocument> news = CouchDBDHCalcDatabase.getInstance().getNews();
            List<NewsItem> result = new Vector<NewsItem>(news.size());

            for (NewsDocument doc : news) {
                result.add(new NewsItem(doc.getText()));
            }

            return result.toArray(new NewsItem[0]);

        } catch (RuntimeException e) {
            log.log(Level.SEVERE, "Exception", e);
            throw e;
        } catch (Exception e2) {
            log.log(Level.SEVERE, "Exception", e2);
            throw new RuntimeException(e2);
        }
    }

    public static void terminate() {
        log.info("terminate");

    }

    public static void initialize() {
        log.info("initialize()");
        CouchDBDHCalcDatabase.getInstance();
        CouchDBDHCalcParameters.getInstance();
    }
}