Tuesday, February 5, 2013

Tomcat(Servlet) JSE RMI application migration to Weblogic – leverage OEPE

1. Introduction

In this blog entry I'll go through the necessary steps of migrating an old fashion application from Tomcat/RMI Server to Weblogic. The original application was developed using Eclipse and contains an UI Servlet layer deployed on Tomcat and the business logic is a JSE application exposing the business logic via RMI. The Servlets are doing RMI calls against the exposed business methods.
I'll go through all the steps regarding the Eclipse -> OEPE migration and through migrating the Servlet/RMI server entirely to WLS by taking different approaches. There will be also some case studies regarding the approaches taken.
The current example project contains:
  • The UI layer = Servlets that are running on Tomcat.
    • TestServlet allow a user to add key/value entries in the session until Submit is pressed.
    • When submit is pressed the user is sent to the second servlet(DBUpdateConfirmationServlet) where the key/value pairs are taken from the session and are passed via RMI to the RMI server for updating a database.
  • RMI Server wraps with RMI the business logic. The business logic just contains methods for DB operations.
You can start with the attached eclipse project workspace. You will also need the SAMPLE schema project installed on a database that you have access to. You can find here also the migrated eclipse project workspace.

2. Migration Scenarios

2.1. Migrate the Servlet app from Tomcat to Weblogic
1. Create a Weblogic Server Connection in Eclipse OEPE
You will create the connection from the local machine where OEPE is running to the Weblogic (Admin and Cluster) running on a different location or locally. You still need to have a Weblogic installation (not domain configuration) on your local machine where OEPE is running.
This should be your final result:
You can see there both the AdminServer and the WLS Cluster.
Let’s try to redeploy our servlet app on the Weblogic Cluster:
This is caused by the fact that the Eclipse Project JVM is 1.7 and the WLS JVM is 1.6. To Solve that go to Project Properties -> Project Facets and change from Java 1.7 to Java 1.6.
Now you can deploy it and you’ll notice that the deployment will be done against the AdminServer, not against the cluster. We will deal with it later on.
Right-click on the Servlet app and choose -> Run As -> Run on Server
Now you’ll see that there will be an error in the web page:
This is because the new Context Root was not loaded by the Weblogic Server.
The reason for that is the fact that in Tomcat the context root is specified in server.xml. This configuration file does not have a corresponded in Weblogic Server.

   2: <Context docBase="MySimpleServlet" 

   3: path="/UpdateDatabaseTableEntries" reloadable="true" 

   4: source="org.eclipse.jst.jee.server:MySimpleServlet"/></Host> 

Now let’s specify the context root for weblogic server.
Right click on your WEB-INF folder and select New->Other:
Then choose Oracle -> Weblogic -> Oracle Weblogic Web Module Descriptor:
Then in the “Weblogic Web Module Deployment Descriptor” set the “Context Root” and the “Server Version”.
Now run the application on the Weblogic Server. The application has successfully started:
All the other application configurations, the ones specified as servlet annotations are automatically taken into consideration by Weblogic 12c as Weblogic 12c supports JEE6 Servlet 3.0 annotations.
If we try to run the application and submit an update then we will notice in the weblogic admin log the following error:

There are 2 reasons for this error:

  • In the Servlet RMI client we are still using the localhost as the location for the RMI server when the location should be the IP of the location where the RMI server is running(our host system)

  • We should enable remote RMI clients

I’ve replaced the localhost with the host ip in the DbUpdateConfirmationServlet.java.

I’ve opened the RMI ports on my firewall and the RMI remote client calls should be possible.
Now the application should work in the current status:

  • Servlet application deployed on the Weblogic Admin server running on my VBox vm
  • RMI Server running on the host machine.

Now let’s cluster our Servlet application.
First we need to enable session replication in our Servlet application.
Go to your application “Weblogic Web Module Deployment Descriptor” that is to be found under: /WebContent/WEB-INF/weblogic.xml and open the design view.
Go to Session ->Persistent Sore and choose from store type “Replicated if clustered”.

The corresponding xml entry in weblogic.xml is:

   1: <wls:session-descriptor> 

   2: <wls:persistent-store-type>replicated_if_clustered</wls:persistent-store-type> 

   3: </wls:session-descriptor> 

Deploy your application to the WLS cluster:

  • Undeploy your application from the Admin Server.
  • Deploy it to the WLS cluster

Approach 1 (recommended): Use OEPE for WLS cluster deployment. In OEPE go to the Servers tab and right click on your Weblogic server connection. Choose Properties and then Weblogic->Publishing->Advanced. Move the AdminServer from right to left and your cluster from left to right. Now the default OEPE Weblogic deployment target will be your Weblogic Cluster.

Now you can deploy your application directly from OEPE to the Weblogic Cluster.
Approach 2: Use WLS Admin Console for WLS cluster deployment. Using OEPE, deploy your application to Admin Server. Go to the Admin Console and then to Deployments and the choose “MySimpleServlet” application and navigate to the Targets tab. Uncheck AdminServer and check the lab cluster.

Press Save.
Now the application will not be available anymore on this URL: http://adminserver-ip:adminserver-port/UpdateDatabaseTableEntries/. It will just be available on both of these URLs:
Now let’s test the Weblogic “Session Replication” feature:
A. Let’s open a connection against one of the managed servers: http://ms2-ip:ms2-port/UpdateDatabaseTableEntries/
B. Let’s add 3 updates requests in the session. After you have added 3 entries, you application should look like bellow, with you 3 entries being displayed.

C. Now, in the same browser change the URL to the other managed server and add a new entry. You should see that the new entry has been added to a list that contains the entries added on the application deployed on the other managed server.

This will happen also if one of the servers will go down – your application will be highly available as the session will be replicated to a different location in the WLS cluster. In a production environment you will not switch the listening ports and addresses manually in the URLs. In front of the WLS cluster you will have a load balancer that will do automatically load balancing and failover for your application. On Exalogic that load balancer will be called Oracle Traffic Director.
2.2. Migrate the RMI Server to Weblogic

Go to MySimpleRMIServer project and then to the RMIServer.java class. Add the following method:

   1: public static void runServer() throws RemoteException,

   2:             MalformedURLException

   3:     {

   4:         init();

   5:         LocateRegistry.createRegistry(1099);

   6:         DatabaseFacade dbFacade = new DatabaseFacadeImpl();

   7:         Naming.rebind("server.dbFacade", dbFacade);

   8:         System.out.println("server.RMI Server is ready.");

   9:     }

Then right-click on MySimpleServlet project and go to Properties -> Java Build Path and add the MySimpleRMIServer project to Projects tab:
Then add the following method to your TestServlet.java:

   1: @Override

   2:     public void init(ServletConfig config) throws ServletException

   3:     {

   4:         super.init(config);

   5:         try

   6:         {

   7:             RMIServer.runServer();

   8:         }

   9:         catch (RemoteException e)

  10:         {

  11:             // TODO Auto-generated catch block

  12:             e.printStackTrace();

  13:         }

  14:         catch (MalformedURLException e)

  15:         {

  16:             // TODO Auto-generated catch block

  17:             e.printStackTrace();

  18:         }

  19:     }

Set the loadOnStartup priority to 2 for the update confirmation Servlet:

   1: @WebServlet(name = "DbUpdateConfirmationServlet", displayName = "Confirm Update Servlet", urlPatterns = { "/ConfirmUpdate" }, loadOnStartup = 2)

Create a new Project -> Java EE -> Enterprise Application Project and name it “MyServletRMIEnterpriseProject”.
Press Next and include both of the projects (MySimpleRMIServer and MySimpleServlet) as modules to this JEE Enterprise project.
Press Finish.
You will notice that the generated project contains 1 bounded library – MySimpleRMIServer.jar and 1 module(web) – MySimpleServlet.war.
Right-click on the MyServletRMIEnterpriseProject and choose New -> Weblogic Deployment Plan.
Press Next.
Name your deployment plan: MyDeploymentPlan.xml
Press Next. Target your Enterprise application:
Press Finish.
From the Weblogic Deployment Plan design view go to Modules ->MysimpleServlet.war -> weblogic-web-app and delete the 2XPath entries.
Now run your Enterprise Application on WLS Cluster. Go to your servers tab inside OEPE and right click on your Weblogic connection and choose Publish and then publish your enterprise application and press Finish.
Now you should see your Enterprise project deployed to your cluster:
My WLS cluster contains 2 Managed Servers that are running on the same physical server. I’ve deployed my RMI Server to 2 WLS Managed Servers running on the same machine and now you should see one exception in one of the 2 WLS Managed Servers log saying that the RMI port 1099 is already in use.
In my case, on my Managed Server 1 MS_1 log I see the following exception:

   1: Connecting to the database... 

   2: java.rmi.server.ExportException: Port already in use: 1099; nested exception is: 

   3: java.net.BindException: Address already in use 

   4: at sun.rmi.transport.tcp.TCPTransport.listen(TCPTransport.java:310) 

Off course that having my Managed Servers running on different physical machines will only solve this issue. Routing, load balancing and failing over Servlet RMI calls is still unaddressed. In the next chapters we will address this. For the moment just access the servlet on one of the 2 managed servers and check if the updates are successfully performed – they should be:
· http://ms1-ip:ms1-port/UpdateDatabaseTableEntries/
· http://ms2-ip:ms2-port/UpdateDatabaseTableEntries/

2.3. Call the RMI exposed methods directly, without using RMI

Inside the Servlet init method, just call initialize the DB connection by using the init() RMIServer method. Do not create a RMI server anymore.

   1: @Override

   2:     public void init(ServletConfig config) throws ServletException

   3:     {

   4:         super.init(config);

   5:         RMIServer.init();

   6:         /*

   7:          * try { //RMIServer.runServer(); } In your

   8:          * DbUpdateConfirmationServlet.java do not use anymore RMI to call your

   9:          * update method. Just call it directly. //DatabaseFacade dbFacade =

  10:          * (DatabaseFacade) registry //.lookup("server.dbFacade");

  11:          * DatabaseFacadeImpl dbFacadeImpl=new DatabaseFacadeImpl(); …

  12:          */

  13:     ...    

  14:     }

  15: ...

  16: dbFacadeImpl.updateCustomerName(Integer.parseInt(idString), newName);

Re-publish the EAR application to the WLS cluster. You should not see the error anymore.

   1: Connecting to the database... 

   2: java.rmi.server.ExportException: Port already in use: 1099; nested exception is: 

   3: java.net.BindException: Address already in use 

   4: at sun.rmi.transport.tcp.TCPTransport.listen(TCPTransport.java:310) 

Now you can use a load balancer to load your http calls against your servlets deployed in either of the 2 WLS Managed Servers. Then the ex-RMI calls(currently just method calls) are done against the logic deployed on the same Managed Server.
The issues in this approach are:

  • You cannot target different containers, different size containers for your Servlet engine and you Enterprise Search engine. There can be the situation where you need less resources for the Servlet Engine(just 1 cluster with 2 managed servers) then for your Business Logic(1 cluster with 10 Managed Servers). Both clusters can be under the same domain.

  • You might want to apply different performance and tuning settings for your Business Logic Calls.

  • You might also want an automatic load-balancer and failover implemented and managed by WLS Cluster itself for the Servlet – Business Logic calls.

The answer to the issues above is that instead of using RMI for wrapping the Enterprise Search Engine, we can use EJB for this purpose. We will show that in the next chapter.
2.4. Wrap your application business logic using EJB instead of RMI

In OEPE create a new EJB project called MySimpleEJBWrapper.
Add the MySimpleRMIServer as project reference to MySimpleEJBWrapper.
Create a new SessionBean to expose you business logic with the following name and content:

   1: package oracle.test.ejbs;


   3: import java.rmi.RemoteException;

   4: import javax.ejb.LocalBean;

   5: import javax.ejb.Stateless;

   6: import oracle.test.connections.DatabaseConnection;

   7: import oracle.test.impl.DatabaseFacadeImpl;


   9: /**

  10:  * 

  11:  * Session Bean implementation class MyEJBWrapper

  12:  */


  14: @Stateless(mappedName = "MyEJBWrapper")

  15: @LocalBean

  16: public class MyEJBWrapper

  17: {

  18:     DatabaseConnection databaseConnection;


  20:     /**

  21:      * 

  22:      * Default constructor.

  23:      * 

  24:      * 

  25:      * 

  26:      * @throws RemoteException

  27:      */

  28:     public MyEJBWrapper() throws RemoteException


  30:     {

  31:         super();

  32:         databaseConnection = new DatabaseConnection();

  33:         databaseConnection.initCreateAndGetDbConnection();

  34:     }


  36:     public String getUpdatedCustomer(int id) throws RemoteException, Exception

  37:     {

  38:         return databaseConnection.getUpdateResult(id);

  39:     }


  41:     public void updateCustomerName(int id, String name) throws Exception

  42:     {

  43:         databaseConnection.updateCustomerName(id, name);

  44:     }

  45: }

Go to MySimpleServlet project and add the MySimpleEJBWrapper project to java build path project dependencies and remove MySimpleRMIServer Project.
Inject the EJB in my DBUpdateConfirmation.java servlet as:
@EJB MyEJBWrapper myEjBWrapperBean;
And call the update method directly from the EJB: myEjBWrapperBean.updateCustomerName(Integer.parseInt(idString), newName);
Republish your application to the WLS cluster:
This is how your deployment should look in WLS admin console:
Test your application.
2.5. Instead of creating the DB connection in your code, create a datasource on the WLS server and use that datasource for DB operations

Create a new datasource in WLS and deploy it to the WLS cluster. The jndi name should be jdbc/sample. Some db connection example:

  • DB name: XE
  • Hostname: localhost
  • Port: 1521
  • DB User name: sample
  • PWD= sample

Create a method in your DatabaseConnection.java class, RMIServer project to get this datasource via jndi name:

   1: public Connection setJndiDbConnection(String jndiName)

   2:     {

   3:         java.sql.Connection connection = null;


   5:         try

   6:         {

   7:             javax.naming.Context initialContext = new javax.naming.InitialContext();

   8:             javax.sql.DataSource dataSource = (javax.sql.DataSource) initialContext

   9:                     .lookup(jndiName);

  10:             //"java:comp/env/jdbc/hrconnDS"

  11:             connection = dataSource.getConnection();

  12:             return connection;

  13:         }

  14:         catch (Exception e)

  15:         {

  16:             e.printStackTrace();

  17:             // or handle more gracefully

  18:         }

  19:         return null;

  20:     }

Use this new connection for DB statements.

   1: public void updateCustomerName(int id, String name) throws Exception

   2:     {

   3:         //Connection connection = MY_DB_CONNECTION;

   4:         Connection connection = setJndiDbConnection("jdbc/sample");


   6:         Statement statement = connection.createStatement();

   7:         statement.executeUpdate("update sample.customer set customer.name='"

   8:                 + name + "' where customer.customer_id=" + id);

   9:         statement.close();

  10:         connection.close();

  11:     }

This new approach will allow you to better tune, manage, administer and monitor your DataSource by using the tools that WLS provides.

3. Monitoring the application with JRockit Mission Control and JRockit Flight Recorder

This chapter is just meant to give you an idea about the value that JRMC monitoring can bring to you. In the next rows I’ll highlight some of the most important features in the context of the example application that I’ve used in the first chapters.
Just a few monitoring examples:

  • Servlet monitoring:


  • EJB monitoring:


  • JDBC monitoring – TO BE CONTINUED

No comments:

Post a Comment