/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.sun.enterprise.ee.server.group.core;
import com.sun.enterprise.ee.server.group.Message.Route;
import com.sun.enterprise.ee.server.group.MessageAggregator;
import com.sun.enterprise.server.ApplicationServer;
import com.sun.enterprise.server.ServerContext;
import com.sun.enterprise.config.ConfigException;
import com.sun.enterprise.config.serverbeans.ClusterHelper;
import com.sun.enterprise.config.serverbeans.Server;
import com.sun.enterprise.config.serverbeans.ServerHelper;
import com.sun.enterprise.ee.cms.core.GroupManagementService;
import com.sun.enterprise.ee.cms.core.GMSFactory;
import com.sun.enterprise.ee.cms.impl.client.JoinNotificationActionFactoryImpl;
import com.sun.enterprise.ee.server.group.Barrier;
import com.sun.enterprise.ee.server.group.Message;
import com.sun.enterprise.ee.server.group.MessageFactory;
import com.sun.enterprise.ee.server.group.MessageReceiver;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Basic messaging runtime that can be used for sending messages and aggregating
* the responses.
*
* A subsystem need to create the ServerMessageRuntime by calling
* ServerMessageRuntime.create(). It can then use the initiateMessaging method
* to setup the messaging. The ServerMessageRuntime will use CallBack to inform
* the subsystem about the status of the messaging.
*
* FIX ME: Some methods doesnt need to be public....
*
* @author Binod.
*/
public class ServerMessageRuntime {
private GroupManagementService gms = null;
private String thisInstance = null;
private boolean active = false;
private String componentName = null;
private MessageReceiver msgReceiver;
private Logger __logger = null;
private Map<Message.Route, Map<String, MessageAggregator>> routeMaps
= new HashMap();
private String presetElectedInstance;
private Elector elector;
/**
* Create a ServerMessageRuntime with a component name and Logger.
*
* @param componentName
* @param logger
*/
private ServerMessageRuntime(String componentName, Logger logger){
assert logger != null;
this.__logger = logger;
init(componentName);
}
/**
* Static method to create a ServerMessageRuntime. The subsystem that
* creates the ServerMessageRuntime would usually keep it as a singleton.
*
* @param name
* @param logger
* @return an instance of ServerMessageRuntime.
*/
public static ServerMessageRuntime create(String name, Logger logger) {
return new ServerMessageRuntime(name, logger);
}
/**
* Appserver user can use
* org.jvnet.glassfish.messagegroup.<componentname>.ElectedInstance property
* to statically select the instance for notification.
*
* @return Statically selected instance name. Null, if it is not set by
* the administrator.
*/
private String getPresetElectedInstance() {
return this.presetElectedInstance;
}
/**
* Initializes the ServerMessageRuntime.
*
* @param componentName
*/
private void init(String componentName) {
this.componentName = componentName;
ServerContext ctx = ApplicationServer.getServerContext();
thisInstance = ctx.getInstanceName();
boolean clustered = false;
this.presetElectedInstance = System.getProperty
("org.glassfish.messagegroup."+componentName+".ElectedInstance");
try {
clustered = ServerHelper.isServerClustered
(ctx.getConfigContext(), thisInstance);
}catch (ConfigException ce) {
this.__logger.log(Level.SEVERE, ce.getMessage(), ce);
}
// If the instance is not clustered, do nothing.... Subsystem that use
// the ServerMessageRuntime does not need to worry about that.
if (clustered == false) {
this.active = false;
return;
}
// Add all the cluster instances to a Set.
String clusterName = null;
Set servers = new HashSet();
try {
clusterName = ClusterHelper.getClusterForInstance
(ctx.getConfigContext(), thisInstance).getName();
Server[] serversArray = (Server[]) ServerHelper.getServersInCluster
(ctx.getConfigContext(), clusterName);
for (Server server: serversArray) {
servers.add(server.getName());
}
} catch (ConfigException ce) {
__logger.log(Level.SEVERE, ce.getMessage(), ce);
}
// If GMS is not enabled, do nothing.
if(!GMSFactory.isGMSEnabled(clusterName)) {
__logger.log(Level.INFO,
"GMS is not enabled, ServerMessageRuntime will not proceed for "+
this.componentName);
this.active = false;
return;
}
// Setup a basic data structure table for all routes available.
// Each route will have its own datastructure for separate house keeping.
for (Message.Route route: Message.Route.values()) {
routeMaps.put(route, new ConcurrentHashMap());
}
try {
this.gms = GMSFactory.getGMSModule(clusterName);
this.msgReceiver =
new MessageReceiver(this.componentName, thisInstance, this);
JoinCallBackImpl jcb =
new JoinCallBackImpl(this.gms, servers, __logger);
this.gms.addActionFactory(msgReceiver,this.componentName);
JoinNotificationActionFactoryImpl jna =
new JoinNotificationActionFactoryImpl(jcb);
if (__logger.isLoggable(Level.FINE)) {
__logger.fine(clusterName +
" - Added Message Receiver :" + this.componentName);
}
this.gms.addActionFactory(jna);
// Now lets wait for every instance to join the cluster;
jcb.waitForEveryone();
this.gms.removeActionFactory(jna);
__logger.fine("Message Runtime is Initialized");
} catch (Exception e) {
__logger.log(Level.SEVERE,e.getMessage(),e);
this.active = false;
}
this.active = true;
}
public Logger getLogger() {
return this.__logger;
}
/**
* Return the elected instance. If the pre-selected instance is
* present, then that is given preference.
*
* @return
*/
public String getElectedInstance() {
String electedInstance = getPresetElectedInstance();
if (electedInstance == null) {
__logger.finer("Elected Instance is not preset");
electedInstance = getElector().getElectedInstance();
}
if (__logger.isLoggable(Level.FINER)) {
__logger.finer("Selected the elected instance " + electedInstance);
}
return electedInstance;
}
/**
* Return the elector instance.
* @return
*/
public Elector getElector() {
if (this.elector == null) {
this.elector = new ElectorImpl(gms,this);
}
return this.elector;
}
/**
* A subsystem can set its own elector implementation.
*
* @param el
*/
public void setElector(Elector el) {
this.elector = el;
}
/**
* Check whether the running instance is the elected instance or not.
* @return
*/
public boolean electedSelf() {
return getElectedInstance().equalsIgnoreCase(thisInstance);
}
/**
* Return the Message aggregator for a particular route/key.
*/
public MessageAggregator getMessageAggregator
(String key, Message.Route route) {
synchronized (this) {
Map<String, MessageAggregator> map =
this.routeMaps.get(route);
if (map.containsKey(key) == false) {
map.put(key, createMA(key, route));
}
return map.get(key);
}
}
/**
* Creates a MessageAggregator instance.
*/
private MessageAggregator createMA(String key, Route route) {
MessageAggregator ma = new MessageAggregator(key, getServers(route), this);
ma.setRoute(route);
return ma;
}
/**
* Returns an existing MA instance or creates a new one.
*/
private MessageAggregator obtainMA(String key, Route route) {
MessageAggregator mr = null;
synchronized (this) {
Map<String, MessageAggregator> aggregatorMap =
routeMaps.get(route);
if (aggregatorMap.containsKey(key) == false) {
mr = createMA(key, route);
aggregatorMap.put(key, mr);
} else {
mr = aggregatorMap.get(key);
}
}
return mr;
}
/**
* When messaging for a particluar route/key is completed,
* remove it from the house keeping.
*/
public void release(String key, Route route) {
synchronized (this) {
Map<String, MessageAggregator> map =
this.routeMaps.get(route);
if (map.containsKey(key)) {
map.remove(key);
}
}
}
public void purge(String key) {
for (Map<String, MessageAggregator> m : routeMaps.values()) {
if (m.containsKey(key)) {
m.remove(key);
}
}
}
/**
* Return the appropriate list of servers from where messages are
* expected. For an ALLTOONE route, the messages are expected from
* all instances except the current one.
*
* For ONETOALL messages are expeted only from the elected instance.
*/
private List getServers(Message.Route route) {
List<String> al = new ArrayList();
switch (route) {
case ALLTOONE :
List<String> members =
this.gms.getGroupHandle().getCurrentCoreMembers();
for (String member : members) {
if (! member.equals(getElectedInstance()))
al.add(member);
}
return al;
case ONETOALL :
al.add(this.getElectedInstance());
return al;
}
return null;
}
/**
* Used by the subsystems. This method waits until the messaging is
* over for a particular route/key. "key" in case of deployment is
* the application name.
*
* The CallBack interface passed in by the subsystem will be used to
* send notifications about the messaging status.
*/
public void initiateMessagingAndWait(String key,
Message.Route route, CallBack cb) {
Barrier b = initiateMessaging(key, route, cb);
if (b != null) {
b.start();
}
}
/**
* Used by the subsystems. This method waits until the messaging is
* over for a particular route/key. "key" in case of deployment is
* the application name.
*
* The CallBack interface passed in by the subsystem will be used to
* send notifications about the messaging status.
*
* Barrier is returned to the subsystem so that, it can wait for the
* completion on a different thread.
*/
public Barrier initiateMessaging(String key,
Message.Route route, CallBack cb) {
// If runtime is not initialized, for eg: in developer profile, return
// null;
if (!active) return null;
/**
* If the current instance is the one elected for sending notifications
* then for ONETOALL route, it will just send messages to all others
* in the cluster. For ALLTOONE, it will wait for message from others
* in the cluster. So, barrier will be raised.
*
* If the current instance is not the elected instance, for ONETOALL, it
* will wait for the message from other instances with the same
* route/key. For ALLTOONE, it will send a message to the elected
* instance and proceed.
*/
if (electedSelf()) {
if (__logger.isLoggable(Level.FINE)) {
__logger.log(Level.FINE, "This instance is elected for sending"+
" messages");
}
switch (route) {
case ONETOALL :
Message mesg = MessageFactory.createMessage(route);
mesg.setAggregationKey(key);
try {
cb.messagingCompleted();
if (__logger.isLoggable(Level.FINE)) {
__logger.fine("Sending ...." + mesg.toString());
}
gms.getGroupHandle().
sendMessage(this.componentName, mesg.toBytes());
} catch (Exception e) {
e.printStackTrace();
// FIX ME.
}
break;
case NONE :
// No ROUTE lets timeout...
if (cb != null)
cb.messagingTimedOut();
break;
case ALLTOONE :
MessageAggregator mr = obtainMA(key, route);
mr.setCallBack(cb);
return mr.getBarrier();
}
} else {
switch (route) {
case ALLTOONE :
Message mesg = MessageFactory.createMessage(route);
mesg.setAggregationKey(key);
try {
if (__logger.isLoggable(Level.FINE)) {
__logger.fine("Sending ALLTOONE ...."
+ mesg.toString() + "To " + getElectedInstance());
}
//gms.getGroupHandle().sendMessage
//(this.componentName, getElectedInstance(), mesg.toBytes());
gms.getGroupHandle().sendMessage
(this.componentName, mesg.toBytes());
} catch (Exception e) {
e.printStackTrace();
// FIX ME.
}
break;
case ONETOALL :
MessageAggregator mr = obtainMA(key, route);
return mr.getBarrier();
}
}
return null;
}
}