Skip to content

Commit

Permalink
Add user client information to user metrics info (language, device cl…
Browse files Browse the repository at this point in the history
…ass, name).

Add metrics logging for server start up, user disconnect, and card judging events.
  • Loading branch information
ajanata committed Feb 24, 2017
1 parent e1bc2c4 commit 45690c1
Show file tree
Hide file tree
Showing 16 changed files with 252 additions and 59 deletions.
3 changes: 3 additions & 0 deletions WebContent/license.html
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ <h3>jQuery Cookie plugin</h3>

<h2>Server</h2>

This product includes GeoLite2 data created by MaxMind, available from
<a href="http://www.maxmind.com">http://www.maxmind.com</a>.

<h3>ANTLR</h3>
Copyright (c) 2010 Terence Parr
<br /> All rights reserved.
Expand Down
10 changes: 10 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -353,5 +353,15 @@
<artifactId>geoip2</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>net.sf.uadetector</groupId>
<artifactId>uadetector-core</artifactId>
<version>0.9.22</version>
</dependency>
<dependency>
<groupId>net.sf.uadetector</groupId>
<artifactId>uadetector-resources</artifactId>
<version>2014.10</version>
</dependency>
</dependencies>
</project>
26 changes: 19 additions & 7 deletions src/main/java/net/socialgamer/cah/RequestWrapper.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
/**
* Copyright (c) 2012, Andy Janata
* Copyright (c) 2012-2017, Andy Janata
* All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
*
* * Redistributions of source code must retain the above copyright notice, this list of conditions
* and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
Expand All @@ -30,15 +30,15 @@

/**
* Wrap around an {@code HttpServletRequest}, to allow parameters to be retrieved by enum value.
*
*
* @author Andy Janata ([email protected])
*/
public class RequestWrapper {
private final HttpServletRequest request;

/**
* Create a new RequestWrapper.
*
*
* @param request
* An {@code HttpServletRequest} to wrap around.
*/
Expand All @@ -48,7 +48,7 @@ public RequestWrapper(final HttpServletRequest request) {

/**
* Returns the value of a request parameter as a String, or null if the parameter does not exist.
*
*
* @param parameter
* Parameter to get.
* @return Value of parameter, or null if parameter does not exist.
Expand All @@ -57,6 +57,18 @@ public String getParameter(final AjaxRequest parameter) {
return request.getParameter(parameter.toString());
}

/**
* Returns the value of a request header as a String, or {@code null} if the header does not
* exist.
*
* @param header
* Header to get.
* @return Value of header, or {@code null} if header does not exist.
*/
public String getHeader(final String header) {
return request.getHeader(header);
}

/**
* If there is an {@code X-Forwarded-For} header, the <strong>first</strong> entry in that list
* is returned instead.
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/net/socialgamer/cah/StartupUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@
import javax.servlet.ServletContextEvent;

import net.socialgamer.cah.CahModule.ServerStarted;
import net.socialgamer.cah.CahModule.UniqueId;
import net.socialgamer.cah.cardcast.CardcastModule;
import net.socialgamer.cah.cardcast.CardcastService;
import net.socialgamer.cah.metrics.Metrics;
import net.socialgamer.cah.task.BroadcastGameListUpdateTask;
import net.socialgamer.cah.task.UserPingTask;

Expand Down Expand Up @@ -131,6 +133,10 @@ public void contextInitialized(final ServletContextEvent contextEvent) {
reconfigureLogging(contextEvent.getServletContext());
reloadProperties(contextEvent.getServletContext());
CardcastService.hackSslVerifier();

// log that the server (re-)started to metrics logging (to flush all old games and users)
injector.getInstance(Metrics.class).serverStarted(
injector.getInstance(Key.get(String.class, UniqueId.class)));
}

public static void reloadProperties(final ServletContext context) {
Expand Down
11 changes: 7 additions & 4 deletions src/main/java/net/socialgamer/cah/data/ConnectedUsers.java
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@ public ErrorCode checkAndAdd(final User user) {
logger.warn(String.format("Unable to get address for user %s (hostname: %s)",
user.getNickname(), user.getHostname()), e);
}
metrics.newUser(user.getPersistentId(), user.getSessionId(), geo);
metrics.newUser(user.getPersistentId(), user.getSessionId(), geo, user.getAgentName(),
user.getAgentType(), user.getAgentOs(), user.getAgentLanguage());

return null;
}
Expand All @@ -162,7 +163,7 @@ public void removeUser(final User user, final DisconnectReason reason) {
synchronized (users) {
if (users.containsKey(user.getNickname())) {
logger.info(String.format("Removing user %s because %s", user.toString(), reason));
user.noLongerVaild();
user.noLongerValid();
users.remove(user.getNickname().toLowerCase());
notifyRemoveUser(user, reason);
}
Expand All @@ -181,7 +182,7 @@ public User getUser(final String nickname) {
}

/**
* Broadcast to all remaining users that a user has left.
* Broadcast to all remaining users that a user has left. Also logs for metrics.
*
* @param user
* User that has left.
Expand All @@ -197,6 +198,8 @@ private void notifyRemoveUser(final User user, final DisconnectReason reason) {
data.put(LongPollResponse.REASON, reason.toString());
broadcastToAll(MessageType.PLAYER_EVENT, data);
}

metrics.userDisconnect(user.getSessionId());
}

/**
Expand Down Expand Up @@ -226,7 +229,7 @@ else if (!u.isAdmin() && System.nanoTime() - u.getLastUserAction() > IDLE_TIMEOU
// Do this later to not keep users locked
for (final Entry<User, DisconnectReason> entry : removedUsers.entrySet()) {
try {
entry.getKey().noLongerVaild();
entry.getKey().noLongerValid();
notifyRemoveUser(entry.getKey(), entry.getValue());
logger.info(String.format("Automatically kicking user %s due to %s", entry.getKey(),
entry.getValue()));
Expand Down
38 changes: 25 additions & 13 deletions src/main/java/net/socialgamer/cah/data/Game.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@
import net.socialgamer.cah.cardcast.CardcastService;
import net.socialgamer.cah.data.GameManager.GameId;
import net.socialgamer.cah.data.QueuedMessage.MessageType;
import net.socialgamer.cah.metrics.Metrics;
import net.socialgamer.cah.task.SafeTimerTask;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.hibernate.Session;

Expand Down Expand Up @@ -109,6 +111,7 @@ public class Game {
private GameState state;
private final GameOptions options = new GameOptions();
private final Set<String> cardcastDeckIds = Collections.synchronizedSet(new HashSet<String>());
private final Metrics metrics;

private int judgeIndex = 0;

Expand Down Expand Up @@ -194,14 +197,16 @@ public Game(@GameId final Integer id, final ConnectedUsers connectedUsers,
final GameManager gameManager, final ScheduledThreadPoolExecutor globalTimer,
final Provider<Session> sessionProvider,
final Provider<CardcastService> cardcastServiceProvider,
@UniqueId final Provider<String> uniqueIdProvider) {
@UniqueId final Provider<String> uniqueIdProvider,
final Metrics metrics) {
this.id = id;
this.connectedUsers = connectedUsers;
this.gameManager = gameManager;
this.globalTimer = globalTimer;
this.sessionProvider = sessionProvider;
this.cardcastServiceProvider = cardcastServiceProvider;
this.uniqueIdProvider = uniqueIdProvider;
this.metrics = metrics;

state = GameState.LOBBY;
}
Expand Down Expand Up @@ -683,10 +688,14 @@ public boolean start() {
options.spectatorLimit, options.scoreGoal, players, currentUniqueId));
// do this stuff outside the players lock; they will lock players again later for much less
// time, and not at the same time as trying to lock users, which has caused deadlocks
final List<CardSet> cardSets;
synchronized (options.cardSetIds) {
blackDeck = loadBlackDeck(session);
whiteDeck = loadWhiteDeck(session);
cardSets = loadCardSets(session);
blackDeck = loadBlackDeck(cardSets);
whiteDeck = loadWhiteDeck(cardSets);
}
metrics.gameStart(currentUniqueId, cardSets, options.blanksInDeck, options.playerLimit,
options.scoreGoal, !StringUtils.isBlank(options.password));
startNextRound();
gameManager.broadcastGameListRefresh();
}
Expand All @@ -698,7 +707,7 @@ public boolean start() {
}
}

private List<CardSet> loadCardSets(final Session session) {
public List<CardSet> loadCardSets(final Session session) {
synchronized (options.cardSetIds) {
try {
final List<CardSet> cardSets = new ArrayList<>();
Expand Down Expand Up @@ -738,12 +747,12 @@ private List<CardSet> loadCardSets(final Session session) {
}
}

public BlackDeck loadBlackDeck(final Session session) {
return new BlackDeck(loadCardSets(session));
public BlackDeck loadBlackDeck(final List<CardSet> cardSets) {
return new BlackDeck(cardSets);
}

public WhiteDeck loadWhiteDeck(final Session session) {
return new WhiteDeck(loadCardSets(session), options.blanksInDeck);
public WhiteDeck loadWhiteDeck(final List<CardSet> cardSets) {
return new WhiteDeck(cardSets, options.blanksInDeck);
}

public int getRequiredWhiteCardCount() {
Expand All @@ -752,21 +761,21 @@ public int getRequiredWhiteCardCount() {

/**
* Determine if there are sufficient cards in the selected card sets to start the game.
* <p>This could be done more efficiently as we're ending up loading the decks multiple times
* with different Sessions, so caching wouldn't help local decks.
*/
public boolean hasEnoughCards(final Session session) {
synchronized (options.cardSetIds) {
if (options.cardSetIds.isEmpty() && cardcastDeckIds.isEmpty()) {
final List<CardSet> cardSets = loadCardSets(session);

if (cardSets.isEmpty()) {
return false;
}

final BlackDeck tempBlackDeck = loadBlackDeck(session);
final BlackDeck tempBlackDeck = loadBlackDeck(cardSets);
if (tempBlackDeck.totalCount() < MINIMUM_BLACK_CARDS) {
return false;
}

final WhiteDeck tempWhiteDeck = loadWhiteDeck(session);
final WhiteDeck tempWhiteDeck = loadWhiteDeck(cardSets);
if (tempWhiteDeck.totalCount() < getRequiredWhiteCardCount()) {
return false;
}
Expand Down Expand Up @@ -1503,6 +1512,9 @@ public void process() {
rescheduleTimer(task, ROUND_INTERMISSION);
}

metrics.roundJudged(currentUniqueId, user.getSessionId(), cardPlayer.getUser().getSessionId(),
playedCards.cardsByUser());

return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
/**
* Copyright (c) 2012, Andy Janata
* Copyright (c) 2012-2017, Andy Janata
* All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
*
* * Redistributions of source code must retain the above copyright notice, this list of conditions
* and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided
* with the distribution.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
Expand All @@ -25,20 +25,19 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.socialgamer.cah.data.WhiteCard;


/**
* Class to track which card(s) have been played by players. Can get the card(s) for a player, and
* also which player played a given card.
*
*
* All methods in this class are synchronized.
*
*
* @author Andy Janata ([email protected])
*/
public class PlayerPlayedCardsTracker {
Expand All @@ -53,7 +52,7 @@ public class PlayerPlayedCardsTracker {

/**
* Add a played card to the mappings.
*
*
* @param player
* Player which played the card.
* @param card
Expand All @@ -71,7 +70,7 @@ public synchronized void addCard(final Player player, final WhiteCard card) {

/**
* Get the {@code Player} that played a card, given the card's ID.
*
*
* @param id
* Card ID to check.
* @return The {@code Player} that played the card.
Expand All @@ -82,7 +81,7 @@ public synchronized Player getPlayerForId(final int id) {

/**
* Determine whether a player has played any cards this round.
*
*
* @param player
* Player to check.
* @return True if the player has played any cards this round.
Expand All @@ -102,7 +101,7 @@ public synchronized List<WhiteCard> getCards(final Player player) {

/**
* Remove and return a player's cards from the played cards tracking.
*
*
* @param player
* Player to remove.
* @return The cards the player had played, or {@code null} if the player had not played cards.
Expand Down Expand Up @@ -143,4 +142,16 @@ public synchronized void clear() {
public synchronized Collection<List<WhiteCard>> cards() {
return playerCardMap.values();
}

/**
* @return A {@code Map} of users to a {@code List} of the cards they played.
*/
public synchronized Map<User, List<WhiteCard>> cardsByUser() {
final Map<User, List<WhiteCard>> cardsByUser = new HashMap<>();
// TODO java8: streams
for (final Map.Entry<Player, List<WhiteCard>> entry : playerCardMap.entrySet()) {
cardsByUser.put(entry.getKey().getUser(), entry.getValue());
}
return Collections.unmodifiableMap(cardsByUser);
}
}
Loading

0 comments on commit 45690c1

Please sign in to comment.