Getting 2 Tomcat servers comminicating with each other - java

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.

Related

Distinguish requests between multiple connectors/ports in embedded Tomcat in Spring Boot

I have a Spring Boot rest service based application configured on multiple ports that needs to distinguish each request between the port it came through. The idea of having several ports for the application is due to different public and private sub-networks (with different security access levels) that could access different parts of the services exposed by the application.
Conceptually the idea was to add additional connectors to the embedded tomcat and then catch all incoming requests altering them by adding a custom header to each one specifying the "channel" it came through.
The problem I'm facing is that I have no idea how I could catch these incoming requests on a connector level (before it gets to any filter or servlet).
So, for the multi-port solution I have:
#Configuration
public class EmbeddedTomcatConfiguration {
#Value("${server.additional-ports}")
private String additionalPorts;
#Bean
public TomcatServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
Connector[] additionalConnectors = additionalConnector();
if(additionalConnectors != null && additionalConnectors.length > 0) {
tomcat.addAdditionalTomcatConnectors(additionalConnectors);
}
return tomcat;
}
private Connector[] additionalConnector() {
if(StringUtils.isNotBlank(additionalPorts)) {
return Arrays.stream(additionalPorts.split(","))
.map(String::trim)
.map(p -> {
Connector connector = new Connector(Http11NioProtocol.class.getCanonicalName());
connector.setScheme("http");
connector.setPort(Integer.valueOf(p));
return connector;
})
.toArray(Connector[]::new);
}
return null;
}
}
In theory, I could register a custom LifecycleListener to each connector, but, as far as I know, it won't help. I've also heard something about valves, though I'm not sure how to implement them per connector.
Or maybe I'm going a completely wrong way.
I'd really appreciate any help in the matter.
It seems as though you have your heart set on trying a Valve, but, after some more research, I would recommend using a ServletFilter to do this work instead of a Valve.
I believe you can do this work in a Valve, but a Valve must be deployed into the tomcat/lib directory instead of being packaged inside of your application. I would urge you to consider trying to keep your application together in deployable artifact instead of having to remember to deploy one extra jar file to your tomcat instance when creating a new deployment.
From this answer, Difference between getLocalPort() and getServerPort() in servlets you should be able to access the tomcat port by calling getLocalPort() on the HttpServletRequest.
Then based on your idea in the question add a specific context name into the request header.
public class PortContextFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
int localPort = request.getLocalPort();
// if else or case statements
ServletRequest newRequest = PortContextHttpServletRequestWrapper(request, YourPortContext)
filterChain.doFilter(newRequest, response);
}
public void destroy() {
// Nothing to do
}
public void init(FilterConfig filterConfig) throws ServletException {
// Nothing to do.
}
}
After that is done, in theory, you should be able to use the #RequestMapping annotation to route based on the name inside the header.
#PreAuthorize("hasPermission(#your_object, 'YOUR_OBJECT_WRITE')")
#RequestMapping("/yourobject/{identifier}", headers="context=<context_name>", method = RequestMethod.POST)
public String postYourObject(#PathVariable(value = "identifier") YourObject yourObject) {
// Do something here...
...
}
One can also use RequestCondition to route requests based on port, which I think is closer to stock spring. see Spring Boot | How to dynamically add new tomcat connector?

Packaging embedded tomcat server

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.

wildfly have a ever running process run in the background

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.

Weblogic Websocket endpoint not working

I'm trying to crete a simple Websocket application based on tutorial here: http://docs.oracle.com/javaee/7/tutorial/doc/websocket004.htm
So the code looks something like this:
#ServerEndpoint("/echo")
public class EchoEndpoint {
#OnMessage
public void onMessage(Session session, String msg) {
try {
session.getBasicRemote().sendText(msg);
} catch (IOException e) { ... }
}
}
I'm running Weblogic 12c. I thought the annotation should be automatically picked up and websocket endpoint created on the address localhost:8888/myApp/echo, but when I try to connect there with this js test: http://www.websocket.org/echo.html, nothing happens. Also when I attach debugger, I see that the EchoEndpoint class was not loaded. What else should I do to make it running? I see nothing else in the Oracle tutorial
The Tyrus library requires a JEE7 container, Weblogic only supports JEE6.
Follow this tutorial to get websockets in Weblogic.
http://docs.oracle.com/middleware/1212/wls/WLPRG/websockets.htm

How to notify all (same) Singleton beans in a Glassfish 3.1 Cluster?

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.

Categories