/*
* Copyright 2004,2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wso2.carbon.core;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.deployment.DeploymentEngine;
import org.apache.axis2.description.TransportInDescription;
import org.apache.axis2.transport.TransportListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.core.multitenancy.utils.TenantAxisUtils;
import org.wso2.carbon.utils.CarbonUtils;
import org.wso2.carbon.utils.ManagementFactory;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.Query;
import javax.management.QueryExp;
import java.lang.management.ManagementPermission;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* Class for handling Server management functionalilty.
* <p/>
* At the moment, handles only maintenance mode operations
*/
public class ServerManagement {
private static final Log log = LogFactory.getLog(ServerManagement.class);
private static final long TIMEOUT = 60 * 1000;
private Map<String, TransportInDescription> inTransports;
private ConfigurationContext serverConfigContext;
public ServerManagement(Map<String, TransportInDescription> inTransports,
ConfigurationContext serverConfigContext) {
this.inTransports = inTransports;
this.serverConfigContext = serverConfigContext;
}
/**
* Method to switch a node to maintenance mode.
* <p/>
* Here is the sequence of events:
* <p/>
* <oll>
* <li>Client calls this method</li>
* <li>The server stops accepting new requests/connections, but continues to stay alive so
* that old requests & connections can be served</li>
* <li>Once all requests have been processed, the method returns</li
* </ol>
*/
public void startMaintenance() throws Exception {
SecurityManager secMan = System.getSecurityManager();
if (secMan != null) {
secMan.checkPermission(new ManagementPermission("control"));
}
log.info("Starting to switch to maintenance mode...");
for (TransportInDescription tinDesc : inTransports.values()) {
TransportListener transport = tinDesc.getReceiver();
transport.stop();
}
log.info("Stopped all transport listeners");
waitForDeploymentThreadCompletion();
waitForRequestCompletion();
}
/**
* Wait until all deployment tasks have completed
*/
private void waitForDeploymentThreadCompletion() {
CarbonUtils.checkSecurity();
log.info("Waiting for deployment completion...");
// Stop all deployment tasks by calling cleanup on the super-tenant & tenant configurators
Map<String, ConfigurationContext> tenantConfigContexts =
TenantAxisUtils.getTenantConfigurationContexts(serverConfigContext);
serverConfigContext.getAxisConfiguration().getConfigurator().cleanup();
for (ConfigurationContext tenantConfigCtx : tenantConfigContexts.values()) {
tenantConfigCtx.getAxisConfiguration().getConfigurator().cleanup();
}
boolean isDeploymentSchedulerRunning;
do {
// Check whether the super-tenant deployment schedulers are running
isDeploymentSchedulerRunning = isDeploymentSchedulerRunning(serverConfigContext);
if (!isDeploymentSchedulerRunning) {
// Check whether tenant deployment schedulers are running
for (ConfigurationContext tenantConfigCtx : tenantConfigContexts.values()) {
isDeploymentSchedulerRunning = isDeploymentSchedulerRunning(tenantConfigCtx);
if (isDeploymentSchedulerRunning) {
break;
}
}
}
} while (isDeploymentSchedulerRunning);
log.info("All deployment tasks have been completed.");
}
private boolean isDeploymentSchedulerRunning(ConfigurationContext configurationContext) {
DeploymentEngine deploymentEngine =
(DeploymentEngine) configurationContext.getAxisConfiguration().getConfigurator();
return deploymentEngine.isDeploymentTaskRunning();
}
/**
* Wait till all service requests have been serviced. This method will only wait for a maximum
* of {@link ServerManagement#TIMEOUT}
*
* @throws Exception If an error occurs while trying to connect to the Tomcat MBean
*/
public void waitForRequestCompletion() throws Exception {
SecurityManager secMan = System.getSecurityManager();
if (secMan != null) {
secMan.checkPermission(new ManagementPermission("control"));
}
log.info("Waiting for request service completion...");
/**
* Get all MBeans with names such as Catalina:type=RequestProcessor,worker=http-9762,name=HttpRequest<n>
* & Catalina:type=RequestProcessor,worker=http-9762,name=HttpsRequest<n>
*/
MBeanServer mbs = ManagementFactory.getMBeanServer();
boolean areRequestsInService;
long start = System.currentTimeMillis();
do {
// Check whether there are any processors which are currently in the SERVICE stage (3)
QueryExp query = Query.eq(Query.attr("stage"), Query.value(3)); // 3 = org.apache.coyote.Constants.STAGE_SERVICE
Set set = mbs.queryNames(new ObjectName("Catalina:type=RequestProcessor,*"), query);
if (set.size() > 0) {
areRequestsInService = true;
if (System.currentTimeMillis() - start > TIMEOUT) {
log.warn("Timeout occurred even though there are active connections.");
break;
}
Thread.sleep(2000);
} else {
areRequestsInService = false;
}
} while (areRequestsInService);
log.info("All requests have been served.");
}
/**
* Method to change the state of a node from "maintenance" to "normal"
*
* @throws Exception If an error occurs while trying to connect to the Tomcat MBean
*/
public void endMaintenance() throws Exception {
SecurityManager secMan = System.getSecurityManager();
if (secMan != null) {
secMan.checkPermission(new ManagementPermission("control"));
}
log.info("Switching to normal mode...");
for (Iterator iter = inTransports.values().iterator(); iter.hasNext();) {
TransportInDescription tinDesc = (TransportInDescription) iter.next();
TransportListener transport = tinDesc.getReceiver();
transport.start();
}
log.info("Switched to normal mode");
}
}