feat: BAEL-5947 External Debugging with JMXTerm (#13847)

* feat: BAEL-5947 External Debugging with JMXTerm

* fix: Removed a faulty test

* fix: Change Random to ThreadLocalRandom

* fix: Renamed a method

* fix: Formatting fix
This commit is contained in:
Eugene Kovko 2023-04-17 05:14:50 +02:00 committed by GitHub
parent 737430655c
commit ae04ab2510
10 changed files with 284 additions and 0 deletions

View File

@ -0,0 +1,16 @@
package com.baeldung.jmxterm;
import java.util.UUID;
public abstract class AbstractPlayerMBean implements PlayerMBean{
private final UUID id;
protected AbstractPlayerMBean() {
this.id = UUID.randomUUID();
}
String getId() {
return getName() + id.toString();
}
}

View File

@ -0,0 +1,42 @@
package com.baeldung.jmxterm;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanNotificationInfo;
import javax.management.Notification;
import javax.management.NotificationBroadcaster;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
public abstract class BroadcastingGuessGame implements NotificationBroadcaster, GuessGameMBean {
private final NotificationBroadcasterSupport broadcaster =
new NotificationBroadcasterSupport();
private long notificationSequence = 0;
private final MBeanNotificationInfo[] notificationInfo;
protected BroadcastingGuessGame() {
this.notificationInfo = new MBeanNotificationInfo[]{
new MBeanNotificationInfo(new String[]{"game"}, Notification.class.getName(), "Game notification")
};
}
protected void notifyAboutWinner(Player winner) {
String message = "Winner is " + winner.getName() + " with score " + winner.getScore();
Notification notification = new Notification("game.winner", this, notificationSequence++, message);
broadcaster.sendNotification(notification);
}
public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) {
broadcaster.addNotificationListener(listener, filter, handback);
}
public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException {
broadcaster.removeNotificationListener(listener);
}
public MBeanNotificationInfo[] getNotificationInfo() {
return notificationInfo;
}
}

View File

@ -0,0 +1,19 @@
package com.baeldung.jmxterm;
import java.util.Arrays;
import java.util.List;
public class GameRunner {
public static void main(String[] args) {
MBeanGameServer mBeanGameServer = new MBeanGameServer();
Player bob = new Player("Bob");
Player alice = new Player("Alice");
Player john = new Player("John");
List<Player> players = Arrays.asList(bob, alice, john);
GuessGame guessGame = new GuessGame(players);
mBeanGameServer.registerGame(guessGame);
guessGame.start();
}
}

View File

@ -0,0 +1,80 @@
package com.baeldung.jmxterm;
import static com.baeldung.jmxterm.RandomNumbergenerator.generateRandomNumber;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class GuessGame extends BroadcastingGuessGame {
public static final int SECOND = 1000;
private static final Logger log = Logger.getLogger(GuessGame.class.getCanonicalName());
private final List<Player> players;
private volatile boolean isFinished = false;
private volatile boolean isPaused = false;
public GuessGame(List<Player> players) {
this.players = players;
log.setLevel(Level.WARNING);
}
public void start() {
int randomNumber = generateRandomNumber();
while (!isFinished) {
waitASecond();
while (!isPaused && !isFinished) {
log.info("Current random number is " + randomNumber);
waitASecond();
for (Player player : players) {
int guess = player.guessNumber();
if (guess == randomNumber) {
log.info("Players " + player.getName() + " " + guess + " is correct");
player.incrementScore();
notifyAboutWinner(player);
randomNumber = generateRandomNumber();
break;
}
log.info("Player " + player.getName() + " guessed incorrectly with " + guess);
}
log.info("\n");
}
if (isPaused) {
log.info("Game is paused");
}
if (isFinished) {
log.info("Game is finished");
}
}
}
@Override
public void finishGame() {
isFinished = true;
}
@Override
public void pauseGame() {
isPaused = true;
}
@Override
public void unpauseGame() {
isPaused = false;
}
public List<Player> getPlayers() {
return players;
}
private void waitASecond() {
try {
Thread.sleep(SECOND);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,8 @@
package com.baeldung.jmxterm;
public interface GuessGameMBean {
void finishGame();
void pauseGame();
void unpauseGame();
}

View File

@ -0,0 +1,37 @@
package com.baeldung.jmxterm;
import java.lang.management.ManagementFactory;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
public class MBeanGameServer {
public static final String ID_FORMAT = "com.baeldung.jmxterm:type=%s,id=%s";
private final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
public void registerPlayer(AbstractPlayerMBean player) {
registerBean(player, "player", player.getId());
}
public void registerGame(GuessGame guessGame) {
registerBean(guessGame, "game", "singlegame");
guessGame.getPlayers().forEach(this::registerPlayer);
}
private void registerBean(Object bean, String type, String id) {
try {
ObjectName name = new ObjectName(String.format(ID_FORMAT, type, id));
server.registerMBean(bean, name);
} catch (InstanceAlreadyExistsException |
MBeanRegistrationException |
NotCompliantMBeanException |
MalformedObjectNameException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,33 @@
package com.baeldung.jmxterm;
import static com.baeldung.jmxterm.RandomNumbergenerator.*;
public class Player extends AbstractPlayerMBean {
private final String name;
private int score = 0;
public Player(String name) {
super();
this.name = name;
}
@Override
public int guessNumber() {
return generateRandomNumber();
}
public void incrementScore() {
score++;
}
@Override
public int getScore() {
return score;
}
@Override
public String getName() {
return name;
}
}

View File

@ -0,0 +1,10 @@
package com.baeldung.jmxterm;
public interface PlayerMBean {
int guessNumber();
int getScore();
String getName();
}

View File

@ -0,0 +1,18 @@
package com.baeldung.jmxterm;
import java.util.concurrent.ThreadLocalRandom;
public class RandomNumbergenerator {
private static final int MIN = 0;
private static final int MAX = 10;
private static final ThreadLocalRandom RANDOM = ThreadLocalRandom.current();
private RandomNumbergenerator() {
}
public static int generateRandomNumber() {
return RANDOM.nextInt(MIN, MAX + 1);
}
}

View File

@ -0,0 +1,21 @@
package com.baeldung.jmxterm;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class PlayerUnitTest {
@Test
void givenNewPlayer_thenScoreIsZero() {
Player player = new Player("John");
assertEquals(0, player.getScore());
}
@Test
void givenNewPlayer_whenIncrementScore_thenScoreIsOne() {
Player player = new Player("John");
player.incrementScore();
assertEquals(1, player.getScore());
}
}