I'm making a html5/js game that will have online capabilities for my backend I've decided to use a wildfly server. The client will communicate with the server via web sockets.
I intended for my wildfly server to also be in charge of game logic decisions such as moving npc's. My plan was to have a #startup bean that would run a server game loop to handle this logic. The server loop would then talk to the serverEndPoint via HornetQ. My serverEndPoint and server loop look like this:
ServerEndPoint
#ServerEndpoint(value= "/game/{user-id}")
public class GameEndpoint {
#Inject
GameManager gameState;
GameWorld gameWorld;
Player p;
private Logger logger = Logger.getLogger(this.getClass().getName());
#OnOpen
public void onOpen(Session session){
//do stuff
}
#OnMessage
public void onMessage(Session session, String message){
//do stuff
}
#OnClose
public void onClose(CloseReason reason){
//do stuff
}
#OnError
public void error(Throwable t){
//do stuff
}
}
GameWorld
#Startup
#Singleton
public class GameWorld {
#Inject
GameManager gameState;
private Logger logger = Logger.getLogger(this.getClass().getName());
#PostConstruct
public void init(){
gameloop();
}
private void gameloop(){
while(true){
logger.info("This is a test!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#PreDestroy
public void terminate(){
//do stuff
}
}
the issue with this is that the server loop freezes everything as it is a infinite loop(for instance if I try and access the html web page I get a 404). obviously this could be solved if the serverLoop was on its own seperate thread but after doing some research it seems threading in jboss is very difficult as its hard to know what dependencies to inject e.t.c
Can anyone shed some light on how I can solve this issue? any help on the matter would be amazing.
What you have encountered has to do with what Java EE is and what it not is: Java EE is optimized for handling many concurrent, short-lived requests, each of which (usually) handle a single transaction. The containers do that very well, particularly with stateless beans, but also with stateful beans (cluster replication etc). As such, Java EE might be well-suited to process the requests coming from your HTML5/JS clients and feed the requests to the messaging infrastructure. Java EE is not, however, designed for long running, thread-blocking background processes like yours.
FWIW: Another issue that you have not yet encountered is, even if you could get that one fixed: Next you'll encounter the transaction timeout on your #PostConstruct method.
I think you are better of with moving the game engine out of the Java EE stack. You already mentioned you plan to use HornetQ - then why not put the game engine in a simple standalone application that receives messages from HornetQ and feeds replies back into HornetQ.
Another option might be a dedicated Java game server engine, see, e.g., this question and its accepted answer on programmers.stackoverflow.com. (Update: it seems the "RedDwarf Server" project mentioned in that answer was discontinued 3 years ago).
If you absolutely want to use the Java EE environment, I suggest you use a TimerService instead. Note, however, that this also requires that your game loop calculation is quick and guaranteed to finish until the next timeout is scheduled, otherwise the container will skip the scheduled invocation (with a "still running" message or similar).
Finally, let me mention that if I were to start a new game server today, I would definitely take a look at Akka, Node.js or similar projects that support "reactive" programming.
Related
First time ever using tomcat/setting up a webapp from scratch so please be mercyful. I have created an embedded tomcat server which basically looks like this:
public class Server implements Runnable {
private Tomcat tomcat;
public Server() {
tomcat = new Tomcat();
tomcat.setPort(8080);
tomcat.addWebapp("", new File("src/webapp").getAbsolutePath());
}
#Override
public void run() {
try {
tomcat.start();
tomcat.getServer().await();
} catch (LifecycleException e) {
e.printStackTrace();
}
}
And I run it in a main that looks like this:
public static void main(String[] args) throws Exception {
Thread thread = new Thread(server);
thread.start();
Foo foo = new Foo();
(thread.isAlive()) {
foo.doStuff();
TimeUnit.HOURS.sleep(interval);
}
}
The purpose of the program is to run the http-server on one thread while the class Foo does some stuff in the backend once every so-and-so hours. Probably not the correct way to create a webapp, but it's the best I've managed.
However, now that I'm trying to package it I'm running into issues because once it is packaged (using Maven) the Server doesn't seem to be able to find the webapp-folder. After a couple hours of googling and trying out a a lot of stuff involving war:s and jar:s I've come to the conclusion that there is something about this embedded tomcat stuff that I'm not understanding.
So, my questions are:
Is the way I've implemented my webapp correct? I'm getting the feeling that it's not but I can't really confirm it.
2a. If incorrect, how does one do it correctly?
2b. If correct, how does one package it into a runnable jar/war?
This is a little bit of a non-standard way of going about it. Rather than writing all your own application logic to handle an integrated web server, would you not consider leveraging something that's already there? You can create a Java project in Spring boot which contains its own embedded web server.
There's a sample starter example here that should get you going - https://spring.io/guides/gs/serving-web-content/
I would recommend this approach rather than writing it yourself as Spring Boot is an industry standard that is widely used, proven, and tested.
I have a JAX-RS REST service which needs to provide some visual server side output when a REST endpoint is invoked. Later on, I might also want to provide a user interface which an administrator will use for interacting with the web service. I could obviously build REST endpoints for this interaction, and have the administrator invoke these (from a different machine or the same machine) using a provided client application with a UI, but I would like to avoid exposing this functionality to the network. In order to make deployment easy, I would also like everything (i.e. the web service and its administrator UI) to be part of the same application.
I have found that my JAX-RS application will throw a HeadlessException if I try to construct a JFrame or any other top level UI element as described here. I have also found that I can avoid this by setting the system property -Djava.awt.headless=false.
I think I may be able to achieve what I need by setting the headless system property as above and defining a singleton startup EJB that will handle all server side UI tasks:
#Singleton
#Startup
public class UiBean {
private JFrame frame;
#PostConstruct
public void init() {
SwingUtilities.invokeLater(() -> {
frame = new JFrame();
// ...setup UI...
});
}
// JAX-RS resources will inject the UiBean and invoke methods as this one when UI updates are needed.
public void updateSomeUiComponent() {
SwingUtilities.invokeLater(() -> { ... });
}
}
I realise that this design is not ideal. Are there other ways of achieving my requirements? The software being built is a prototype and time is of the essence, so I would like to avoid having to learn complete new technologies (already invested a lot in learning JAX-RS and JPA). Quick-and-dirty is ok, I guess I'm just looking for the least dirty solution ;).
Thanks in advance!
In my test application (JSF, PrimeFaces, Wildfly 8) I have a simple messaging interface for writing a message and viewing those that were sent to me. I now would like to create an automatic respond for those messages that were not answered after 5 minutes. This automatic respond is meant to be independent of any person sitting behind the browser window and clicking the refresh button.
My current idea is to create a new class annotated with #ApplicationScoped. This class shall run an individual thread handling the responding process. Would that be a good approach? Or is there a functionality somewhere that already covers this matter?
I think the option worth considering would be to use javax.ejb.TimerService:
#Stateless
public class AutomaticResponseSender {
private static final Integer _5_MINS = 300000;
#Resource
private TimerService timerService;
public void waitFiveMinutes() {
Timer nextTimer = timerService.createSingleActionTimer(_5_MINS, null);
}
#Timeout
public void autoRespondForNotAnswered() {
// here send an auto response
}
public void cancel() {
timerService.cancel();
}
}
Upon sending the manual answer you need to call cancel() to avoid autoresponse or, alternatively, you can check in autoRespondForNotAnswered() method if the answer was send.
Another approach could be to use #Schedule and, let's say every 5 or 10 seconds, check for messages not answered in 5 mins and sending automatic answers for them.
I have a JEE6 application that runs on an Glassfish 3.1.2 cluster.
One #Singleton Bean contains some kind of (readolny) cache. A User can press a button in the GUI to update the cache with (updated) content from the database.
This works well in a none clustered environment, but now we need to switch to an cluster.
So I am facing the problem, that when a user press that update button, only the Cache Singleton from his server node is updated. My question is, what would be the easiest way to make the other Singletons (in the other nodes) updating there data too?
I am aware of question Singleton in Cluster environment, but my question is specific for Glassfish (because I hope there is some build in support), the other one is taged with "Websphere". And my question is about JEE6, the other one is older than JEE6.
GlassFish High Availability Administration Guide explicitly states:
Restrictions
When configuring session persistence and failover, note the following
restrictions:
When a session fails over, any references to open files or network connections are lost. Applications must be coded with this restriction in mind.
EJB Singletons are created for each server instance in a cluster, and not once per cluster.
Another suggestion, would be to use JMS and have the GUI button press post a message to a JMS Topic. All the Singleton beans can subscribe to that Topic and receiving the message will cause them all to update from the database, almost simultaneously. The benefit of this approach, is that it leverages more of the built in features of Glassfish, without necessarily having to bring in another framework.
In any case, moving from single instance to multiple instance is never a truly seamless change, and is going to cause some difficulty. There will probably need to be application changes to make sure that all the relevant state (other than session state), is shared correctly to all instances in the cluster.
Unfortunately there's no built-in way of achieving what you want, but the shoal framework that Glassfish bases its clustering on could help you out here. You can solve the problem either by sending notifications to cluster members to update their caches or by replacing your current cache with a distributed one.
Below is an example using shoal to send notifications:
#Startup
#Singleton
public class Test {
private String groupName = "mygroup";
private String serverName = System.getProperty("HTTP_LISTENER_PORT");
private GroupManagementService gms;
#PostConstruct
public void init() {
Runnable gmsRunnable = GMSFactory.startGMSModule(serverName, groupName,
GroupManagementService.MemberType.CORE, null);
gms = (GroupManagementService) gmsRunnable;
try {
gms.join();
gms.addActionFactory(new MessageActionFactory() {
#Override
public Action produceAction() {
return new MessageAction() {
#Override
public void consumeSignal(Signal signal)
throws ActionException {
// Update your cache here
}
};
}
}, groupName);
} catch (GMSException e) {
Logger.getAnonymousLogger().severe(e.getMessage());
}
}
#PreDestroy
public void cleanup() {
gms.shutdown(GMSConstants.shutdownType.INSTANCE_SHUTDOWN);
}
/**
* Call this from your button click.
*/
public void updateCache() {
try {
byte[] message = new byte[] {};
gms.getGroupHandle().sendMessage(groupName, message);
} catch (GMSException e) {
Logger.getAnonymousLogger().severe(e.getMessage());
}
}
}
If you wanted to use a distributed cache instead:
DistributedStateCache cache = gms.getGroupHandle().getDistributedStateCache();
Items placed in the cache will be replicated to the other cluster nodes.
Take a look at JGroups. It's a framefork for reliable multicast communication. JBoss clustering mechanisms are currently based on this tool.
You can check out an example usage of JGroups here.
I have a Java web application I’m developing for a school project. There’s a requirement to have the presentation tier (servlets/jsp) be deployed to one server and business logic be deployed to another.
I need a solution to connecting the 2 servers.
Currently I’ve researching RMI and Axis2.
I’m not able to get RMI successfully working. I’m following official tutorial and keep getting a security exception locally, and imagine that it will get worse when Tomcat is involved.
Axis2 seems like a good solution, but I will need time to ramp up on it.
My question is: there a simple way of connection 2 servers so that I can call my business layer? Maybe Tomcat has something built-in.
If RMI is the de-facto protocol and API I should use, is there any good tutorials on using RMI with Tomcat.
Servers that I’m using are both running Tomcat.
I am not sure how complicated is your data layer but you can implement REST interface on business logic server using Apache CXF, for example. That should be easier than using Axis2.
There are many many options:
Write a file from one side, read it from the other. "the other" has to have an infinite loop to monitor the folder where "one side" writes request files.
Use sockets
Use REST
RMI
If you're on Linux:
Shared memory
Pipes
Given your environment I would go with REST.
I ended up using RMI. Using this tutorial I got it working: http://sacrosanctblood.blogspot.com/2008/11/rmi-tutorial-rmi-and-tomcat.html . The key is: in the start up servlet you have to make sure that the Object that you are stubbing out is class scoped and not method scoped. Also, the Security manager code is not needed.
Here's the code for the startServer servlet that I'm using:
public class startServer extends HttpServlet
{
public static boolean isRegistered = false;
public IRemote service = new RemoteImpl();
#Override
public void init(ServletConfig config) throws ServletException
{
super.init(config);
if (!isRegistered)
{
try
{
IRemote stub = (IRemote) UnicastRemoteObject.exportObject(service, 0);
Registry registry = LocateRegistry.createRegistry(9345);
registry.rebind(IRemote.serviceName, stub);
System.out.println("Remote service bound");
isRegistered = true;
}
catch (Exception e)
{
System.err.println("Remote service exception:");
e.printStackTrace();
}
}
}
}
And here's the client code:
public String getRemoteString()
{
String result = "";
try
{
Registry registry = LocateRegistry.getRegistry(9345);
IRemote serv = (IRemote) registry.lookup(IRemote.serviceName);
result = serv.executeRemote("Test");
}
catch (Exception e)
{
System.err.println("Remoteservice exception:");
e.printStackTrace();
}
return result;
}
Currently it's running all on the same server, but I'm sure that I can get the 2 working at a later time.