Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: BAEL-5947 External Debugging with JMXTerm #13847

Merged
merged 5 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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();
}

}
Original file line number Diff line number Diff line change
@@ -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();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.baeldung.jmxterm;

public interface GuessGameMBean {

void finishGame();
void pauseGame();
void unpauseGame();
}
Original file line number Diff line number Diff line change
@@ -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);
}
}

}
Original file line number Diff line number Diff line change
@@ -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;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.baeldung.jmxterm;

public interface PlayerMBean {

int guessNumber();

int getScore();

String getName();
}
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
@@ -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());
}
}