/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.jvnet.glassfish.comms.clb.core;
import org.jvnet.glassfish.comms.gms.GMSEventListener;
import com.sun.enterprise.ee.cms.core.GroupHandle;
import com.sun.enterprise.ee.cms.spi.MemberStates;
import java.util.ArrayList;
import java.util.Collection;
import java.util.logging.Logger;
import org.jvnet.glassfish.comms.clb.admin.CLBConfigurator;
import org.jvnet.glassfish.comms.util.LogUtil;
import java.util.List;
import java.util.Vector;
import java.util.logging.Level;
import org.jvnet.glassfish.comms.gms.GMSEventListenerException;
import com.sun.enterprise.Switch;
import java.util.Timer;
import java.util.TimerTask;
/**
*
* @author kshitiz
*/
public class ClusterHealthMonitor{
private static final Logger _logger = LogUtil.CLB_LOGGER.getLogger();
private String clusterName;
private CHMGMSEventListenerImpl gmsEventListenerImpl;
//validation will be done after 5000 milliseconds
private static final long VALIDATION_WAIT_TIME = 5000L;
private static final Timer timer = Switch.getSwitch().getTimer();
private TimerTask timerTask;
/** Creates a new instance of ServerHealthMonitor */
public ClusterHealthMonitor(String clusterName)
throws CLBRuntimeException {
this.clusterName = clusterName;
try {
gmsEventListenerImpl = new CHMGMSEventListenerImpl(clusterName);
} catch (GMSEventListenerException ex) {
throw new CLBRuntimeException(ex);
}
}
public List<String> getHealthyInstances() {
final int INITIAL_CAPACITY = 10;
final int INCREMENTAL = 5;
Vector healthyInstances = new Vector(INITIAL_CAPACITY,INCREMENTAL);
GroupHandle groupHandle = gmsEventListenerImpl.
getGroupManagementService().getGroupHandle();
final long startTime = System.currentTimeMillis();
List<String> coreMembers = groupHandle.getCurrentCoreMembers();
for (String core : coreMembers) {
//Changing such that threshold of 6 seconds and
//timeout of 0 seconds is used. This will ensure value
//is returned from maintained GMS state cache.
//These values will later be used as default and this
//change will be reverted back
MemberStates state = groupHandle.getMemberState(core,
6000L, 0L);
if (state == MemberStates.ALIVEANDREADY
|| state == MemberStates.READY) {
healthyInstances.add(core);
} else if (state == MemberStates.UNKNOWN) {
MemberStates pollState = groupHandle.getMemberState(core, 6000L, 3000L);
if ( pollState == MemberStates.ALIVEANDREADY || pollState == MemberStates.READY) {
healthyInstances.add(core);
} else {
long duration = System.currentTimeMillis() - startTime;
if (_logger.isLoggable(Level.FINER)) {
_logger.finer("CLB getHealthyInstances: getMemberState(" + core + ", 6 secs, 3 secs) hb only state=" + state +
" poll state=" + pollState + " getHealthyInstances current duration(in ms)=" + duration);
}
}
}
}
if(_logger.isLoggable(Level.FINE)){
_logger.log(Level.FINE,
"clb.gms_module_core_members",
new Object[]{clusterName, groupHandle.getAllCurrentMembers().toString()});
_logger.log(Level.FINE,
"clb.gms_module_core_current_members",
new Object[]{clusterName, coreMembers.toString()});
_logger.log(Level.FINE,
"clb.gms_module_current_healthy_members",
new Object[]{clusterName, healthyInstances.toString()});
}
return healthyInstances;
}
private ServerInstance getServerInstance(String clusterName,
String instanceName, boolean isHttp) {
Controller controller;
if(isHttp)
controller = CLBConfigurator.getInstance().
getHttpControllerInitializer().getController();
else
controller = CLBConfigurator.getInstance().
getSipControllerInitializer().getController();
if(controller == null)
return null;
return controller.getInstance(clusterName, instanceName);
}
public String getClusterName() {
return clusterName;
}
public void cleanup() {
gmsEventListenerImpl.cleanup();
}
/*
class PingThread extends Thread {
private ServerInstance httpInstance;
private ServerInstance sipInstance;
private final int maxRetryCount = 10;
private final long sleepBetweenRetry = 5000L;
PingThread(ServerInstance httpInstance, ServerInstance sipInstance) {
this.httpInstance = httpInstance;
this.sipInstance = sipInstance;
}
public void run() {
pingHttpEndPoint();
}
private void pingHttpEndPoint() {
EndPoint endPoint;
endPoint = httpInstance.getEndPoint(CLBConstants.HTTP_PROTOCOL);
if (endPoint == null) {
endPoint = httpInstance.getEndPoint(CLBConstants.HTTPS_PROTOCOL);
}
if (endPoint == null) {
_logger.log(Level.SEVERE,
"clb.no_endpoint_to_ping",
new Object[]{httpInstance.getName(), clusterName});
}
sendHttpRequest(endPoint);
}
private void sendHttpRequest(EndPoint endPoint) {
String request = "HEAD / HTTP/1.1\r\n" + "Host: " +
endPoint.getHost() + ":" + endPoint.getPort() + "\r\n" +
"Proxy-ping: true\r\n\r\n";
int count = 0;
while (count < maxRetryCount) {
count++;
try {
Socket socket = new Socket();
socket.connect(endPoint.getSocketAddress());
OutputStream outputStream = socket.getOutputStream();
outputStream.write(request.getBytes());
outputStream.flush();
BufferedReader reader = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
String line = reader.readLine();
if (line != null) {
int index = line.indexOf(" ");
if (index > -1) {
int next = line.indexOf(" ", index + 1);
if (next > -1) {
int statusCode = Integer.valueOf(line.substring(index +
1, next));
if ((statusCode >= 100) && (statusCode <= 499)) {
if(_logger.isLoggable(Level.FINE))
_logger.log(Level.FINE,
"clb.ping_request_successful",
new Object[]{httpInstance.getName(), clusterName});
httpInstance.markAsHealthy();
sipInstance.markAsHealthy();
}
}
}
}
try {
socket.close();
} catch (IOException ex) {
if(_logger.isLoggable(Level.FINE))
_logger.log(Level.FINE,
"clb.caught_an_exception",
ex);
}
if (httpInstance.isHealthy()) {
break;
}
_logger.log(Level.SEVERE,
"clb.ping_request_unsuccessful",
new Object[]{ httpInstance.getName(), clusterName, line});
} catch (UnknownHostException ex) {
_logger.log(Level.SEVERE,
"clb.cannot_open_connection",
new Object[]{httpInstance.getName(), clusterName,
endPoint.getHost(), endPoint.getPort()});
if(_logger.isLoggable(Level.FINE))
_logger.log(Level.FINE,
"clb.caught_an_exception",
ex);
break;
} catch (IOException ex) {
if(_logger.isLoggable(Level.FINE))
_logger.log(Level.FINE,
"clb.caught_an_exception",
ex);
}
try {
Thread.sleep(sleepBetweenRetry);
} catch (InterruptedException ex) {
if(_logger.isLoggable(Level.FINE))
_logger.log(Level.FINE,
"clb.caught_an_exception",
ex);
}
}
if (!httpInstance.isHealthy()) {
_logger.log(Level.SEVERE,
"clb.cannot_mark_instance_healthy",
new Object[]{httpInstance.getName(), clusterName});
}
}
}
*/
class CHMGMSEventListenerImpl extends GMSEventListener{
public CHMGMSEventListenerImpl(String clusterName) throws GMSEventListenerException {
super(clusterName, !CLBConfigurator.getInstance().isSelfLoadbalanced());
}
public void onRecovery(String clusterName, String instanceName,
boolean isClusterStartup) {
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE,
"clb.recieved_recovery_notification_for_instance",
new Object[]{instanceName, clusterName, isClusterStartup});
}
/* Commented ping thread mechanism as it is not needed after
* JoinedAndReadyNotification. However keeping it as a place-holder.
ServerInstance httpInstance = getServerInstance(clusterName, instanceName, true);
ServerInstance sipInstance = getServerInstance(clusterName, instanceName, false);
PingThread pingThread = new PingThread(httpInstance, sipInstance);
pingThread.start();
*/
ServerInstance instance = getServerInstance(clusterName, instanceName, true);
if (instance != null) {
instance.markAsHealthy(isClusterStartup);
}
instance = getServerInstance(clusterName, instanceName, false);
if (instance != null) {
instance.markAsHealthy(isClusterStartup);
createTimerTaskForValidation();
}
}
public void onFailure(String clusterName, String instanceName,
boolean isClusterShutdown) {
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE,
"clb.recieved_failure_notification_for_instance",
new Object[]{instanceName, clusterName, isClusterShutdown});
}
ServerInstance instance = getServerInstance(clusterName, instanceName, true);
if (instance != null) {
instance.markAsUnhealthy(isClusterShutdown);
}
instance = getServerInstance(clusterName, instanceName, false);
if (instance != null) {
instance.markAsUnhealthy(isClusterShutdown);
if(!isClusterShutdown){
createTimerTaskForValidation();
}
}
}
private void validateCLBAndGMSSync(String clusterName){
ServerCluster cluster = CLBConfigurator.getInstance().
getHttpControllerInitializer().getController().
getCluster(clusterName);
Collection<ServerInstance> serverInstances =
cluster.getAllInstances();
List<String> healthyInstancesInGMS = getHealthyInstances();
List<String> healthyInstancesInCLB = new ArrayList<String>();
for(ServerInstance instance:serverInstances){
if(instance.isHealthy()){
healthyInstancesInCLB.add(instance.getName());
}
}
_logger.log(Level.INFO, "clb.healthy_instances_in_gms_view",
new Object[]{clusterName,
healthyInstancesInGMS.toString()});
_logger.log(Level.INFO, "clb.healthy_instances_in_clb_view",
new Object[]{clusterName,
healthyInstancesInCLB.toString()});
if(healthyInstancesInCLB.containsAll(healthyInstancesInGMS)
&& healthyInstancesInGMS.containsAll(healthyInstancesInCLB)){
_logger.log(Level.INFO, "clb.clb_gms_view_in_sync",
new Object[]{clusterName});
}
else{
_logger.log(Level.SEVERE, "clb.clb_gms_view_not_in_sync",
new Object[]{clusterName});
}
}
private synchronized void createTimerTaskForValidation(){
//If timerTask exists, cancel it
if(timerTask != null){
if(timerTask.cancel())
timer.purge();
}
//Create a new timer Task and schedule it
timerTask = new TimerTask(){
public void run(){
validateCLBAndGMSSync(clusterName);
}
};
timer.schedule(timerTask, VALIDATION_WAIT_TIME);
}
/*
public void onDisable(String clusterName, String instanceName) {
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE,
"clb.recieved_disable_notification_for_instance",
new Object[]{instanceName, clusterName});
}
ServerInstance instance = getServerInstance(clusterName, instanceName, true);
if (instance != null) {
instance.disableInstance();
}
instance = getServerInstance(clusterName, instanceName, false);
if (instance != null) {
instance.disableInstance();
}
}
public void onEnable(String clusterName, String instanceName) {
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE,
"clb.recieved_enable_notification_for_instance",
new Object[]{instanceName, clusterName});
}
ServerInstance instance = getServerInstance(clusterName, instanceName, true);
if (instance != null) {
instance.enableInstance();
}
instance = getServerInstance(clusterName, instanceName, false);
if (instance != null) {
instance.enableInstance();
}
}
*/
}
}