Document 16083926

advertisement
PLUG-IN FOR SINGLETON SERVICE IN CLUSTERED ENVIRONMENT
AND
IMPROVING FAILURE DETECTION METHODOLOGY
Srinivasa Chakravarthy Kodali
B.E., Nagarjuna University, India, 2007
PROJECT
Submitted in partial satisfaction of
the requirements for the degree of
MASTER OF SCIENCE
in
COMPUTER SCIENCE
at
CALIFORNIA STATE UNIVERSITY, SACRAMENTO
SPRING
2011
PLUG-IN FOR SINGLETON SERVICE IN CLUSTERED ENVIRONMENT
AND
IMPROVING FAILURE DETECTION METHODOLOGY
A Project
by
Srinivasa Chakravarthy Kodali
Approved by:
, Committee Chair
Chung-E-Wang, Ph.D.
, Second Reader
Ahmed Salem, Ph.D.
Date
ii
Student: Srinivasa Chakravarthy Kodali
I certify that this student has met the requirements for format contained in the University
format manual, and that this project is suitable for shelving in the Library and credit is to
be awarded for the project.
, Graduate Coordinator
Nikrouz Faroughi, Ph.D.
Date
Department of Computer Science
iii
Abstract
of
PLUG-IN FOR SINGLETON SERVICE IN CLUSTERED ENVIRONMENT
AND
IMPROVING FAILURE DETECTION METHODOLOGY
by
Srinivasa Chakravarthy Kodali
In the time of social networking, number of users accessing web applications has
reached millions. As a result the user load on web applications has increased, resulting in
the evolution of different technologies to manage this increase. One such technology is
clustering web application i.e. a large number of servers aka cluster nodes, each hosting
the same web application. Thus, creating a distributed web application where the user
load will be distributed among each of the cluster nodes. However while running an
application in a clustered environment different challenges arise, one of which is
handling singleton objects.
Singleton object is an instance of a class when only one such object is created per
application. In a clustered environment each node runs the same web application creating
its own singleton object as it requires. Thus each of the cluster nodes aka servers, ends up
with one singleton object each. This surplus of singleton objects will cause unnecessary
system overload or repetitive actions of the web application. One way of addressing this
problem is to create a singleton service for the entire cluster which can provide access to
singleton objects for each member of the cluster as and when needed.
iv
The goal of this project is to design a plug-in which can automatically create a
singleton service in any clustered web application. The plug-in should be capable of
starting and stopping singleton service in any one node of the cluster. Once a node
imbibes the singleton service it can cater to all singleton object requirements of the entire
cluster. Since only one node acts as a singleton service it is more susceptible to be a
single point of failure. The way of fixing this problem is by designing the plug-in in such
a way that it detects the failure and immediately chooses another node to become the
singleton service. Therefore providing a singleton service i.e. highly available for
clustered web applications.
, Committee Chair
Chung-E-Wang, Ph.D.
Date
v
DEDICATION
Affectionately Dedicated to
Respected Parents & Teachers
vi
ACKNOWLEDGEMENTS
I take this as a wonderful opportunity to thank Dr. Chung-E Wang and Dr. Ahmed
Salem who gave me the opportunity to work under their guidance. Especially, I would
like to thank professor Wang for his valuable insights and thoughts regarding the project.
I also thank my family and friends for their support and cooperation.
vii
TABLE OF CONTENTS
Page
Dedication ..........................................................................................................................vi
Acknowledgements ........................................................................................................... vii
List of Figures .................................................................................................................... x
Chapter
1. INTRODUCTION ......................................................................................................... 1
2. BACKGROUND .......................................................................................................... 4
2.1 Clustering Implementation............................................................................... 4
2.2 Singleton Service ............................................................................................. 5
3. DESIGN SPECIFICATION .......................................................................................... 7
3.1 System Overview ............................................................................................. 7
4. IMPLEMENTATION .................................................................................................. 11
4.1 Cluster Configuration...................................................................................... 11
4.2 Apache HTTP Load Balancer ......................................................................... 12
4.3 Database Connection Pooling ......................................................................... 14
4.4 Building Singleton Service ............................................................................. 15
4.4.1 RMI Service Configuration........................................................... 15
4.4.2 Plug-in Thread .............................................................................. 16
5. FAILURE DETECTION METHODOLOGY.............................................................. 19
6. CONCLUSION AND PROSPECTS FOR IMPROVEMENT .................................... 22
viii
6.1 Conclusion ...................................................................................................... 22
6.2 Prospects for Improvement ............................................................................. 22
Appendix Source Code ..................................................................................................... 24
Bibliography ..................................................................................................................... 57
ix
LIST OF FIGURES
Page
1.
Figure 2.1 Basic Clustering Architecture ................................................................... 4
2.
Figure 3.1 Plug-in Development Environment .......................................................... 8
3.
Figure 5.1 Application Server Cluster ....................................................................... 19
x
1
Chapter 1
INTRODUCTION
Of the 6.8 billion people in the world today there are around 2 billion internet
users. The internet provides a platform for users to pursue diverse functions ranging from
day to day chores (bills, e-commerce etc.,), business, education, research, networking to
entertainment. Most of these functions are bought to the users by the use of web
applications, which are applications that are accessed over the internet or intranet. Each
web application has a limit on the number of users it can handle at a given time. In
today’s world with the increased number of internet users most web applications are over
loaded in no time, therefore reducing their efficiency.
The two major reasons for reduction in efficiency are either due to lack of
resource availability in the server or bad coding practices. Problems related to coding are
either due to bad design practices or badly written code. Both of which can be resolved
by following the correct software design practices or by fixing the code where ever
necessary. When it comes to problems related to resource availability in the server they
are caused either by one or both of the following; the number of concurrent threads that
the server can handle, the number of socket connections that it can establish etc. The
solution to this is by clustering web application into n number of servers aka, cluster
nodes which are similar in functionality.
Clustering is one of the technologies used to make web applications scale to
millions of users. When clustering of web application is done, as the number of usage
2
instance increases, the load is distributed to each node. This system is called a distributed
system. Designing such a system comes with its own set of challenges in making the
entire system work in a manner similar to a single system. Some of which are handling
user sessions, load balancing, handling singleton objects, etc.
Handling singleton objects in a clustered environment is one of the important
challenges in a distributed system. As per definition of singleton object, there should be
only one instance (aka singleton object) of the class in a complete system. But as the
application is clustered, each cluster node holds its own singleton object. One of the
solutions to this problem is having one node provide access to all singleton objects in a
cluster. This service which provides all singleton objects to all the nodes in the cluster is
called singleton service.
Any application that needs to be clustered and contains singleton objects should
implement singleton service. Therefore every application that is similar to above needs to
develop a singleton service. It is a waste of development cycles to design a distinctive
singleton service for each web application. The goal of this project is to design a code
(plug-in) that is universal, but specific in its function of creating a singleton service in
any clustered environment. With the current web application usage trend this
functionality of the plug-in, in automatically creating a singleton service in any clustered
environment is highly desirable.
A major problem in using this plug-in is that the singleton service runs in only
one node of a cluster, therefore making it a single point of failure. If this node fails for
3
any reason, singleton objects will not be available to any node in the cluster. Thus, the
plug-in should also be capable of starting a singleton service in another node of the
cluster if the node running singleton service fails. By resolving this issue we get a
comprehensive and highly effective singleton service plug-in for clustered web
application.
4
Chapter 2
BACKGROUND
2.1 Clustering Implementation
A cluster is a group of computer systems connected together to provide a solution
or to solve a problem. Computers in a cluster are called nodes.
Application servers
Node-1
L
O
A
D
Presentation
server
B
A
L
A
N
C
E
R
Node-2
Node-3
Figure 2.1 Basic Clustering Architecture
Figure 2.1 shows the basic clustering architecture with a cluster of application
servers. The presentation server is a system that executes user interface related tasks and
the application server is the location where business logic is executed. The presentation
server makes calls to the load balancer, and the load balancer holds some information
5
about the number of application nodes available and their location. This information is
stored in configuration files. Based on the configuration, calls are redirected to
application servers and these servers process the calls and return with a response. Thus,
the load balancer is responsible for distributing load to the different nodes in the cluster.
There are two ways of clustering

Vertical clustering: When multiple instances of the server are run in a single
physical machine, it is called vertical clustering. A drawback of this
implementation is that resources of a single system are shared by multiple
nodes in a cluster.

Horizontal clustering: Running each node of the cluster in its own physical
machine is called horizontal clustering. This is a very expensive solution
because as the nodes increase, the number of physical machines to maintain
also increases.
An effective clustering mechanism is to use both ways. However in this project
only vertical clustering has been used to distribute the load.
2.2 Singleton Service
Singleton objects are single instances of class maintained in a complete
application. These objects are made globally accessible. Singleton objects are static
objects which are instantiated in the static function of the class. Below is the snippet of
code that shows how singleton objects are created:
6
private static instance = null;
public void static getInstance() {
if(instance == null) {
instance = new SingletonObject();
}
return instance;
}
Singleton service is the node in the cluster that provides access of singleton objects to all
the nodes in cluster.
7
Chapter 3
DESIGN SPECIFICATION
3.1 System Overview
A 3-tiered sample web application is needed to create and test a plug-in for
singleton service. As a result the web application is divided into a presentation server, an
application server and a database. The application server is the one that holds the
business logic and the presentation server holds the user interface code and makes http
calls to the application server. As the number of http calls made to the application server
increases, the performance of the application slows down. The only solution is to cluster
the application server so that all the http calls are distributed among the application server
cluster. Apache Tomcat 6.0 is the Servlet container, which is where the application server
and presentation server codes are executed.
Figure 3.1 is the design diagram of the environment used to build the plug-in. The
presentation code is loaded into the browser as I am using javascript and html. From
javascript ajax calls are made to the Apache 2.2 http web server, which acts as a load
balancer with some configuration changes and with the new module called mod_jk. This
can redirect the calls from the load balancer to each tomcat instance that contains the
application server code and, in turn, writes the data from the nodes to the database.
8
Tomcat Instances
L
O
A
D
Browser
B
A
L
A
N
C
E
R
Node-1
MySQL database
Database
Node-2
Node-3
Figure 3.1 Plug-in Development Environment
There are two kinds of load balancers:

Software Load Balancer: A software program that listens to the http calls at a
particular port and redirects calls to the application servers. There are different
strategies in choosing an application server to which http calls should be
redirected. Some of the strategies in selecting an application server are, using
round robin technique or the number of connections already given per node.

Hardware Load Balancer: A physical device that splits all the network calls
across multiple application server nodes. These use strategies similar to those
mentioned above to select the application server.
9
The performance of the hardware load balancer is better when compared to the
software load balancer. In this project, a software load balancer is being used
as it is cost effective and easy to configure.
Singleton service should expose a set of methods that provide singleton objects to
all the nodes in the cluster. One of the best implementations is to make singleton service
as RMI (remote method invocation) service, as it is a core java functionality and works in
all systems that use java. By using this service, all nodes in the cluster can make a remote
call to get access to the singleton objects. But we need to let all the nodes know where
RMI service is currently running by specifying the path of RMI service. In a cluster,
global information can be stored in a shared file system, database, or some cache. So, if
any node wants to access the singleton objects, it needs to look for the RMI service
location and call a particular method in the service.
Initially, no node will be running the RMI service. Server initialization needs to
look up the RMI location and ping that server to determine whether RMI service is
located in a specified location and if the specified location is not available, then the server
should start RMI service in any of the live nodes. Initialization of the RMI service and
verification of whether RMI service is running needs to be synchronized as two or more
servers might be doing the same thing and start RMI service in two different nodes.
If a node running RMI service fails, the application needs to start RMI service in
another node in the cluster. To do this, a ping servlet is used which pings the machine
running the RMI service. This is done by all the nodes in the cluster. If the ping to the
10
node running RMI service fails, it means RMI service is down. We need to reinstate the
initialization of RMI service, which starts a new RMI service in another node. This
makes sure that singleton service is highly available.
11
Chapter 4
IMPLEMENTATION
4.1 Cluster Configuration
As the application is running in a tomcat container, we need to cluster the tomcat
server, which holds the application server code. Every J2EE container has its own
clustering implementation. The tomcat cluster configuration is one of the simplest
configurations of all J2EE containers.
The configuration file that holds the entire configuration variables like ports and
protocols used is in the tomcat container server.xml. As we are implementing vertical
clustering, we create multiple tomcat instances in the same machine where each server
holds the same application server code. All the port conflicts need to be fixed as all
tomcat instances will be running in a single machine. A few more configuration steps are
needed to enable clustering in the tomcat J2EE container:

Step 1: Add the following line in server.xml to help enable communication
between the load balancer and application servers:
<Connector port="8109" protocol="AJP/1.3" redirectPort="8443" />
Apache Jserv Protocol (AJP) is used to communicate between the load balancer
and the application server over TCP connections. This reduces the socket creation time
and keeps the TCP connection live until the request handling cycle is terminated. This is
similar to connection pooling used in a database.
12

Step 2: The line below is responsible for setting up a cluster and provides
callers with a valid multicast receiver/sender:
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>

Step 3: In all the three application server’s web.xml file, add the following
line:
<distributable />
This makes sure the code is suitable to run in a clustered environment. Hence, all new
user sessions and changes to existing user sessions will be replicated in the other
members of the cluster.

Step 4: Add jvmRoute property to the Engine tag in server.xml:
<Engine name=”Catalina” defaultHost=”localhost” jvmRoute=”tomcatA”>
JvmRoute acts as an identifier to the tomcat instances. It will be used by the load balancer
to identify the tomcat worker.
By incorporating the above changes, the tomcat cluster will be available to accept
calls from the load balancer given that a few configuration changes in the load balancer
are made.
4.2 Apache HTTP Load Balancer
Apache Http server loaded with mod_jk is used as a software load balancer for the
tomcat instances. Mod_jk is the connector that establishes communication between the
13
apache http server and tomcat servers. All the configurations related to apache http server
need to be added to the httpd.conf file. The configuration steps are:

Step 1: Add latest Mod_jk binary file into the modules folder of http server

Step 2: Add the following lines to httpd.conf file:
1) LoadModule jk_module modules/mod_jk-apache-2.2.4.so
This action helps in loading the mod_jk file on the http server when it is started.
2) JkWorkersFile=” C:\Program Files (x86)\Apache Software
Foundation\Apache2.2\conf\workers.properties”
Helps in specifying the location of workers.properties file.
3) JkMount /chatAppServer/* loadbalancer
All the calls from the presentation server to the application server are redirected to
the loadbalancer property, which is configured in workers.properties file.

Step 3: Add the following lines to the workers.properties file
worker.tomcatA.port=8109
worker.tomcatA.host=localhost
worker.tomcatA.type=ajp13
This identifies the port, the host of the tomcat instance and the protocol, which
specifies communication between the http server and the tomcat instance. A
similar set of configuration values need to be added for each tomcat instance in
the cluster.
14
4.3 Database Connection Pooling
MySQL database has been used to store the configuration details and all state
information of the singleton service. A database connection is established using the
mysql jdbc connector. There are two ways to establish a database connection and to
query the database. One of them is to open a connection every time we need information.
This can be expensive due to opening of a socket connection and checking credentials
every time a connection is made.
The other way of opening a database connection is to use database connection
pooling (DBCP). In DBCP, a list of connections can be maintained to connect to a
database, these connections can be reused every time a connection is needed. One of the
popular types of database connection pooling is tomcat database connection pooling. For
tomcat to establish a connection pooling datasource, a resource needs to be specified in
conext.xml. The code below makes sure that connection pooling is established for the
application:
<Resource name="jdbc/chatApp" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000" username="root"
password="passenger" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/chatapplication"/>
JNDI lookup is used to get a connection to the database from the pool of
connections.
15
4.4 Building Singleton Service
Now that the application server is clustered and configured with a load balancer,
the system is ready to implement singleton service and test it. A sample chat application
is built, which creates singleton objects and the singleton service is built for these objects.
There are two major parts for creating singleton service. One is configuring RMI service
in a web application and the other one is implementing a thread that initializes the
singleton service.
4.4.1 RMI Service Configuration
Remote method invocation (RMI) is the core java function used for invoking
methods of remote java objects. Thus RMI service is used to access singleton object
methods running in different nodes in the cluster.
All singleton object creation or availability is provided by one class which
extends a Remote java class. This Remote java class makes sure that these methods are
available to the external JVMs. For example in chat application, the http request that
creates a new thread, based on roomId will be done in this class, which extends Remote.
Also, all the chat entries related to that particular room go through this Remote class. So
before calling these methods from any location, we should know where remote service is
running (host address) and the port number of the RMI service which is available in the
database. With this remote service host location and port number we can get access to the
RMI registry.
16
Initialization of the RMI service is done when servers are starting up via the
singleton service plug-in thread. This initialization creates RMI registry, which can be
accessed by all nodes. The RMI registry looks up the remote class object by acquiring the
key that the object is bound to. Once acquired the object can be used to call its methods.
4.4.2 Plug-in Thread
One of the Singleton service tasks is to initialize RMI service in a single node in
the cluster. It is also capable of starting and stopping the RMI service. On the startup of
each server, a servlet thread is initialized in all the nodes in the application responsible
for singleton service tasks. This thread looks into the database for the location of RMI
service and pings the service and checks to see if it is live. If the service is already live,
servlet will not initialize RMI service in any node. If the service is not running in any
node, a thread will initialize RMI service randomly in any node of the cluster.
Initialization of RMI service is done by a plug-in thread calling a servlet that
binds the class, which extends Remote to some key. This is the same key used to retrieve
the object from the RMI registry. A plug-in thread call will pass the port address which is
available in configuration so that RMI is initialized at that port address. Below is a
snippet of code used to bind an object to RMI service:
Public InitRemoteService( int port) {
try {
service = new RemoteServiceImpl();
17
IRemoteService stub = (IRemoteService)
UnicastRemoteObject.exportObject(service, 0);
Registry registry = LocateRegistry.createRegistry(port);
Registry.bind(“singletonSerive”, stub);
}
Catch (Exception e) {
e.printStackTrace();
}
}
The above piece of code will bind the remoteServiceImpl object to a key used for
look-up in the registry.
One of the problems is if all the servers are started at the same time, all the nodes
try to initialize remote service in some location creating multiple RMI services in the
cluster. To avoid this problem, we need to add a distributed lock on a piece of code that
initializes the start of singleton service. There are lots of distributed locking mechanisms
currently in the industry. One of the simple locking mechanisms is Hazelcast distributed
locking. Hazelcast is an open source jar, which provides solutions to distribution
problems. Once the Hazelcast jar is included and all the nodes are configured into the
configuration.xml, we get access to all the distributed solutions provided by Hazelcast.
Below is a snippet of code that shows how distributed locking is implemented:
Lock lock = Hazelcast.getLock(obj);
18
lock.lock();
try {
… ….
}finally {
lock.unlock();
}
This piece of code locks the try block if the same class in some other node holds a
lock on the object. Once the lock is released from the node holding it, the next node that
requested for the lock gets a hold of the lock and goes on. But once the RMI service is
initialized by the first node, the database is updated with the location at which RMI
service is running. This makes sure a new RMI service cannot be initialized in other
nodes, as every time before RMI service is initialized we look to see whether service is
running fine. Therefore at any give point of time only one RMI service is running in the
cluster.
19
Chapter 5
FAILURE DETECTION METHODOLOGY
If the node running the singleton service fails, no node has access to singleton
objects in the cluster; thus, it is a single point of failure for the application. To overcome
a failure of a node running singleton service, a plug-in should automatically detect the
failure and start singleton service in another node. This ensures that singleton service is
highly available.
Node - 2
Node-1
Node3
Node-4
Figure 5.1 Application Server Cluster
Let us say we have four application server nodes running in a cluster and node-2
running singleton service dies because of network failure or for any other reason. The
20
nodes in the cluster should be able to pick up another node as the singleton service. In a
clustered environment, the simplest way to find whether a node is running or not is by
pinging the nodes in small intervals of time. The same is applied to find out whether
singleton service is running or not. Each node running in the cluster will ping the node
running the singleton service. This ensures that even if two or more nodes fail, singleton
service is always available. As we are already saving the information regarding where the
singleton service is running, each node gets access to this information and starts pinging
the singleton service URL.
For implementation of the ping servlet, the same servlet thread that starts the RMI
service is used. Thus, the thread in this servlet will see whether singleton service is
running every 2 seconds. Below is the snippet of code that pings the given url:
U = new URL(url);
Uc =u.openconnection();
is = new InputStreamReader(uc.getInputStream());
bin = new BufferedReader(is);
bin.readLine();
The code above opens a URL connection for the provided singleton service url and
returns some data back from the provided url. This proves that the provided server is live
and available.
If one node figures out that the singleton service node is not responding to the
ping, then that thread will get the Hazelcast distributed lock on the code and start
21
singleton service in some random available node. Once the RMI service is started, the
database is updated with host location, and the port where the singleton service is
running. While starting the singleton service in a new node due to service failure, some
state information can be passed on to the new singleton service if required. By
implementing this ping servlet we ensure that one node in the cluster will always be
running the singleton service. Therefore making the singleton service highly available.
22
Chapter 6
CONCLUSION AND PROSPECTS FOR IMPROVEMENT
6.1 Conclusion
From the conception of this project, the aim was to develop a singleton service
plug-in that is highly available and not restricted to a single J2EE container. During the
development process many issues (which have been elaborated in previous chapters)
were resolved so as to achieve the above goal. This was done using open source
software’s like tomcat container, apache http load balancer and Hazelcast. However, the
plug-in is independent of any of these software’s, as it is built using RMI, servlets and
core java functionalities except for Hazelcast, which is used for the distributed locking
system. The above singleton service plug-in works well for all large-scale as well as
small-scale J2EE applications. Whereas this is not the case with other singleton service
providers like JBOSS and Weblogic, their singleton services are tightly tied up with their
containers and cannot be used in other J2EE containers and are not open source.
6.2 Prospects for Improvement

A distributed JNDI look-up can be implemented by the plug-in to look up
service information such as, which cluster node is running, ports, and
configurations.
23

Failure detection methodology can be improved because in the current
implementation, each node in the cluster pings the singleton service node,
which causes unnecessary traffic between nodes.

When a singleton node dies, a plug-in should be capable of passing state
information of the failed node to a new singleton service node.
24
APPENDIX
Source Code
/**
*The plug-in class responsible for starting singleton service. Ping servlet is also part of
*this class which checks whether the singleton node is running in the cluster
*environment.
*/
package plugin;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
25
import java.rmi.registry.Registry;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import com.hazelcast.core.Hazelcast;
import rmi.IRemoteService;
import chatApp.DBconnection;
public class PingJVMThread extends Thread {
private String remoteServiceUrl = null;
private SampleLock obj = new SampleLock();
public static void startThread() {
new PingJVMThread().start();
26
}
public void init() {
this.setName("pingjvmthread");
}
/**
* Infinite loop which keeps on pinging the server which is running the singleton remote
*service in cluster check if the "/ping" returning "alive" else get distributed lock to start a
* new singleton remote service. if some one already got the lock update
*"remoteServiceUrl" with new location where remote service is running
*/
public void run() {
init();
while(true) {
//TODO: remoteServiceUrl - this is server which is running remote
service -populate this from configuration file
Lock lock = Hazelcast.getLock(obj);
lock.lock();
try{
remoteServiceUrl = readRemoteServiceUrl();
27
if(remoteServiceUrl == null || remoteServiceUrl.length() == 0 ||
!makeUrlConnection(remoteServiceUrl+"/ping").equals("alive") || getRemoteService()
== null) {
try {
remoteServiceUrl = startRemoteServiceInCluster();
} catch (Exception e) {
System.err.println("remote service is already running in cluster");
e.printStackTrace();
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
lock.unlock();
}
}
}
28
/**
* start a new singleton remote service in cluster
* See if any jvm is alive and check whether remove service on that machine is
already running
* if not call "startremoteservice" servlet on that JVM
* @throws Exception
*/
private String startRemoteServiceInCluster() throws Exception{
String runningJVM = null;
//read from configuration location list of jvms running
Map<String, Integer> JVMnRMIMap = new HashMap<String, Integer>();
JVMnRMIMap.put("http://localhost:8081/chatAppServer", 9345);
JVMnRMIMap.put("http://localhost:8082/chatAppServer", 9346);
JVMnRMIMap.put("http://localhost:8083/chatAppServer", 9347);
//check whether jvm is alive
Set<String> jvms = JVMnRMIMap.keySet();
for(String jvmuri : jvms) {
if(makeUrlConnection(jvmuri+"/ping").equals("alive")){
runningJVM = jvmuri;
break;
29
}
}
//if jvm is live check whether remoteservice is running in that location
IRemoteService service = (IRemoteService)rmiLookUp(null,
JVMnRMIMap.get(runningJVM), IRemoteService.serviceName);
if(service == null) {
makeUrlConnection(runningJVM+"/startremoteservice?port="+JVMnRMIMap.g
et(runningJVM));
} else {
throw new Exception();
}
//TODO:update configuration where rmi service is running
//write to db
updateRemoteServiceUrl(runningJVM,
JVMnRMIMap.get(runningJVM));
getRemoteService().startReadThread(0);
return runningJVM;
}
private String makeUrlConnection(String url) {
30
String inputLine = "dead";
URLConnection uc;
BufferedReader bin = null;
InputStreamReader is = null;
InputStream inputstream = null;
URL u = null;
try {
u = new URL(url);
uc = u.openConnection();
inputstream = uc.getInputStream();
is =new InputStreamReader(inputstream);
bin = new BufferedReader(is);
inputLine = bin.readLine();
bin.close();
} catch (MalformedURLException e) {
} catch (IOException e) {
}
return inputLine;
}
private Object rmiLookUp(String url, int port, String serviceName) {
31
Registry registry;
try {
registry = LocateRegistry.getRegistry(url, port);
return registry.lookup(serviceName);
} catch (AccessException e) {
e.printStackTrace();
} catch (RemoteException e) {
System.out.println("Unable to find RMI service at port:" + port);
} catch (NotBoundException e) {
e.printStackTrace();
}
return null;
}
private String readRemoteServiceUrl() {
Connection con = DBconnection.getConnection();
String url = null;
try {
ResultSet rs = con.createStatement().executeQuery("SELECT
rmiurl FROM CONFIGURATION");
while(rs.next()) {
32
url = rs.getString(1);
}
} catch (SQLException e) {
e.printStackTrace();
}
DBconnection.releaseConnection(con);
return url;
}
private void updateRemoteServiceUrl(String url, int port) {
Connection con = DBconnection.getConnection();
try {
PreparedStatement preparedStmt = con.prepareStatement("update
configuration set rmiurl=?, port=? where id=1");
preparedStmt.setString(1, url);
preparedStmt.setInt(2, port);
preparedStmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
DBconnection.releaseConnection(con);
33
}
private IRemoteService getRemoteService() {
Connection con = DBconnection.getConnection();
//TODO:Read from configuration where rmi is running. port is hard coded
for now
IRemoteService service = null;
try{
int port = 9345;
ResultSet rs = con.createStatement().executeQuery("SELECT port
FROM CONFIGURATION");
while(rs.next()) {
port = rs.getInt(1);
}
Registry registry = LocateRegistry.getRegistry(null,port);
service = (IRemoteService)
registry.lookup(IRemoteService.serviceName);
} catch (Exception e) {
System.err.println("Remoteservice exception: RMI is not
running");
34
}
DBconnection.releaseConnection(con);
return service;
}
}
/* Class responsible for initializing RMI service in a particular cluster node.*/
package rmi;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
public class InitRemoteService {
public static IRemoteService service;
public InitRemoteService(int port){
try {
service = new RemoteServiceImpl();
IRemoteService stub =
(IRemoteService) UnicastRemoteObject.exportObject(service, 0);
Registry registry = LocateRegistry.createRegistry(port);
35
registry.rebind(IRemoteService.serviceName, stub);
System.out.println("Remote service bound");
} catch (Exception e) {
System.err.println("Remote service exception:");
e.printStackTrace();
}
}
}
/*Servlet responsible for calling to initialize the remote service object*/
package servlets;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import rmi.InitRemoteService;
public class StartRemoteService extends HttpServlet{
@Override
36
public void init(ServletConfig config) {
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp){
new InitRemoteService(Integer.parseInt(req.getParameter("port")));
}
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) {
doGet(req, resp);
}
}
/*Class returning a response that the cluster node is live */
package servlets;
import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
37
import javax.servlet.http.HttpServletResponse;
import plugin.PingJVMThread;
/**
* (1). This servlet is responsible for pinging all the JVM in a cluster and starting a thread
on the process of servlet to do this.
* (2). Provide a servlet method (doGet) that responds to all the ping calls coming from
other instances
* @author skodali
*
*/
public class PingServlet extends HttpServlet{
@Override
public void init(){
PingJVMThread.startThread();
}
@Override
38
public void doGet(HttpServletRequest req, HttpServletResponse res) {
try {
res.getWriter().print("alive");
} catch (IOException e) {
e.printStackTrace();
}
}
public void doPost(HttpServletRequest req, HttpServletResponse res) {
doGet(req, res);
}
}
/* RMI service interface. These methods are called for any in the cluster. Thus, these
methods are *available only through remote service
*/
package rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface IRemoteService extends Remote{
39
public final String serviceName = "RMIService";
public void startReadThread(int roomId) throws RemoteException;
public void stopReadThread() throws RemoteException;
public void enterChat(String username, String chat, int roomId) throws
RemoteException;
}
/*
*RMI service interface implementation where the singleton objects are initialized.
*/
package rmi;
import java.rmi.RemoteException;
import java.util.Random;
import chatApp.ReadChatsThread;
import chatApp.WriteChats;
40
public class RemoteServiceImpl implements IRemoteService {
@Override
public void startReadThread(int roomId) throws RemoteException{
ReadChatsThread.getInstance().startReadThread(roomId);
}
@Override
public void stopReadThread() throws RemoteException{
//TODO: see how we can stop thread
}
@Override
public void enterChat(String username, String chat, int roomId) throws
RemoteException {
WriteChats.getInstance().write(username, chat, roomId);
}
}
/* Chat example restful webservices class. All the methods is this class are available as
*webservice methods to the external world
*/
41
package restfulWS;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.sql.Connection;
import java.sql.ResultSet;
import java.util.Random;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import chatApp.DBconnection;
import rmi.IRemoteService;
42
@Path("/chatapp")
public class ChatWS{
/**
*All members who hit join will call this method, user will be subscribed to the
channel where
*chats will be pushed to. This method creates a new room or a new read Thread
* @throws RemoteException
*/
@GET
@Path("/createRoom")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public String createRoom() throws RemoteException {
getRemoteService().startReadThread(0);
//TODO: subscribe to the channel for pushing messages
return "true";
}
/**
43
* members entering room number will be automatically pushed into a currently
running thread
* he or she will receive chats related to that room.
* @throws RemoteException
*/
@GET
@Path("/joinroom")
@Consumes(MediaType.APPLICATION_JSON)
public void joinRoom(@QueryParam(value="roomid") int roomId) throws
RemoteException{
//TODO: subscribe user to particular roomid and he or she will start
receiving chats related to that room
}
/**
* write entered chats to DB
* @throws RemoteException
*/
@GET
@Path("/enterchat")
@Consumes(MediaType.APPLICATION_JSON)
44
@Produces(MediaType.APPLICATION_JSON)
public String enterChat(@QueryParam(value = "username") String username,
@QueryParam(value = "chat") String chat, @QueryParam(value="roomid") int roomId)
throws RemoteException {
getRemoteService().enterChat(username, chat ,roomId);
return "true";
}
/**
* looks up RMI service and returns its object
* @return IRemoteService Object
*/
public IRemoteService getRemoteService() {
Connection con = DBconnection.getConnection();
//TODO:Read from configuration where rmi is running. port is hard coded
for now
IRemoteService service = null;
try{
int port = 9345;
ResultSet rs = con.createStatement().executeQuery("SELECT port
FROM CONFIGURATION");
45
while(rs.next()) {
port = rs.getInt(1);
}
Registry registry = LocateRegistry.getRegistry(null,port);
service = (IRemoteService)
registry.lookup(IRemoteService.serviceName);
} catch (Exception e) {
System.err.println("Remoteservice exception:");
e.printStackTrace();
}
DBconnection.releaseConnection(con);
return service;
}
}
/*Chat example class which Write chats to database
*/
package chatApp;
import java.sql.Connection;
46
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class WriteChats {
private static final String writeQuery = "INSERT INTO chatapplication.chatdata
VALUES (?, ?, ?, ?)";
private static WriteChats instance = null;
public static WriteChats getInstance() {
if(instance == null) {
instance = new WriteChats();
}
return instance;
}
public void write(String username, String chat, int roomId) {
try {
Connection connection = DBconnection.getConnection();
47
PreparedStatement pstmt1 =
connection.prepareStatement(writeQuery);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd
hh:mm:ss");
String messageUserName =
username+System.currentTimeMillis();
String message = chat+System.currentTimeMillis();
pstmt1.setString(1, sdf.format(new Date()));
pstmt1.setString(2, messageUserName);
pstmt1.setString(3, message);
pstmt1.setInt(4, roomId);
pstmt1.executeUpdate();
DBconnection.releaseConnection(connection);
} catch(SQLException e) {
e.printStackTrace();
}
}
}
/*Chat example thread responsible for reading the chats from the database and pushing
them to UI
*/
48
package chatApp;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ReadChatsThread {
private static ReadChatsThread instance = null;
public static ReadChatsThread getInstance() {
if(instance == null) {
instance = new ReadChatsThread();
}
return instance;
}
public void startReadThread(int roomId) {
49
new readThread(roomId).start();
}
/**
*
* thread responsible for reading chat entries for every 1000 ms.
* create a json string for each call and PUSH to web UI
*
* @author skodali
*/
public class readThread extends Thread{
private static final String readQuery = "SELECT * FROM
chatapplication.chatdata WHERE ID >= ? AND ROOMID = ?";
private static final String readLastPopulate = "SELECT lastpopulated
FROM chatapplication.lastpopulated";
private static final String writeLastPopulate = "update
chatapplication.lastpopulated set lastpopulated = ? where id=1";
private static final long READ_WAIT_TIME = 1000;
Connection connection = null;
PreparedStatement pstmt1 = null;
50
PreparedStatement pstmt2 = null;
PreparedStatement pstmt3 = null;
ResultSet readResultSet1 = null;
ResultSet readResultSet2 = null;
ResultSet lastPopulatedResultSet = null;
int roomId = 0;
boolean readTheadInfiniteLoop = true;
public readThread(int roomId) {
this.roomId = roomId;
this.setName("readThread=" + roomId);
}
private void init() {
try {
connection = DBconnection.getConnection();
pstmt1 = connection.prepareStatement(readQuery);
pstmt2 = connection.prepareStatement(readLastPopulate);
pstmt3 = connection.prepareStatement(writeLastPopulate);
} catch (SQLException e) {
51
System.out.println("***** unable to get handle to database
conncetion*******");
e.printStackTrace();
}
}
@Override
public void run() {
this.init();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd
hh:mm:ss");
Timestamp lastPopulatedId = null;
try {
while(readTheadInfiniteLoop) {
readResultSet1 = pstmt2.executeQuery();
while(readResultSet1.next()) {
lastPopulatedId =
readResultSet1.getTimestamp(1);
}
pstmt1.setTimestamp(1, lastPopulatedId);
52
pstmt1.setInt(2, roomId);
readResultSet2 = pstmt1.executeQuery();
pstmt3.setString(1, sdf.format(new Date()));
pstmt3.executeUpdate();
while(readResultSet2.next()) {
String userName =
readResultSet2.getString(2);
String data = readResultSet2.getString(3);
//TODO:create a json string and push it to
web UI using comet connection
System.out.println(userName);
System.out.println(data);
}
Thread.sleep(READ_WAIT_TIME);
}
}
catch (SQLException e) {
System.out.println("***** sql exception*******");
e.printStackTrace();
} catch (InterruptedException e) {
53
System.out.println("***** sleep interrrupted*******");
e.printStackTrace();
}
}
}
}
/*Class responsible for getting access to the database connection pool and get the
connection *object from the pool.
*/
package chatApp;
import java.sql.Connection;
import java.sql.SQLException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
/**
* Class responsible to initialize database connection object
54
* @author skodali
*
* DBCP is the by Tomcat apache commons DBCP
* how to setup resource DBCP is http://tomcat.apache.org/tomcat-6.0-doc/jndiresources-howto.html
*/
public class DBconnection {
static Connection con = null;
static DataSource ds = null;
static {
InitialContext initialContext;
try {
initialContext = new InitialContext();
Context envctx =
(Context)initialContext.lookup("java:comp/env");
ds = (DataSource) envctx.lookup("jdbc/chatApp");
con = ds.getConnection();
if(con != null) {
System.out.print("*********connectionestablished**********8");
55
}
} catch (NamingException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
//
try {
//
Class.forName ("com.mysql.jdbc.Driver").newInstance ();
//
con = DriverManager.getConnection
("jdbc:mysql://localhost:3306/chatapplication", "root", "passenger");
//
} catch (Exception e) {
//
e.printStackTrace();
//
//
}
return con;
try {
return ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
56
}
return null;
}
public static void releaseConnection(Connection connection){
try {
if(connection != null)
connection.close();
} catch(SQLException e) {
e.printStackTrace();
}
}
}
57
BIBLIOGRAPHY
[1] The Apache Software Foundation. (n.d.a). Apache tomcat. Retrieved from
http://tomcat.apache.org/download-60.cgi
[2] The Apache Software Foundation. (n.d.b). Apache tomcat 6.0: Clustering/session
replication how-to. Retrieved from http://tomcat.apache.org/tomcat-6.0doc/cluster-howto.html
[3] The Apache Software Foundation. (n.d.c). Apache tomcat 6.0: JNDI resources howto. Retrieved from http://tomcat.apache.org/tomcat-6.0-doc/jndi-resourceshowto.html#JDBC_Data_Sources
[4] The Apache Software Foundation. (2011). Apache http server project. Retrieved from
http://httpd.apache.org/
[5] Datadisk.com.uk. (n.d.). Tomcat and Apache setup. Retrieved from
http://www.datadisk.co.uk/html_docs/java_app/tomcat6/tomcat6_apache_server.h
tm
[6] Easy Way Server. (n.d.). Implementation of tomcat clustering. Retrieved from
http://www.easywayserver.com/implementation-tomcat-clustering.htm
[7] Jersey.java.net. (n.d.). Jersey 1.5 user guide. Retrieved from
http://jersey.java.net/nonav/documentation/latest/user-guide.html#d4e8
[8] Oracle. (n.d.). Java servlet technology. Retrieved from
http://www.oracle.com/technetwork/java/javaee/servlet/index.html
Download