/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. 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 com.sun.enterprise.ee.admin.configbeans;
import com.sun.enterprise.ee.admin.PortInUseException;
import com.sun.enterprise.ee.admin.PortInUse;
import com.sun.enterprise.ee.admin.PortReplacedException;
import com.sun.enterprise.config.serverbeans.Domain;
import com.sun.enterprise.config.serverbeans.SystemProperty;
import com.sun.enterprise.config.serverbeans.NodeAgents;
import com.sun.enterprise.config.serverbeans.NodeAgent;
import com.sun.enterprise.config.serverbeans.Configs;
import com.sun.enterprise.config.serverbeans.Config;
import com.sun.enterprise.config.serverbeans.Applications;
import com.sun.enterprise.config.serverbeans.ConnectorModule;
import com.sun.enterprise.config.serverbeans.EjbModule;
import com.sun.enterprise.config.serverbeans.J2eeApplication;
import com.sun.enterprise.config.serverbeans.WebModule;
import com.sun.enterprise.config.serverbeans.Resources;
import com.sun.enterprise.config.serverbeans.ConnectorResource;
import com.sun.enterprise.config.serverbeans.JdbcResource;
import com.sun.enterprise.config.serverbeans.Servers;
import com.sun.enterprise.config.serverbeans.Server;
import com.sun.enterprise.config.serverbeans.ApplicationRef;
import com.sun.enterprise.config.serverbeans.ResourceRef;
import com.sun.enterprise.config.serverbeans.ServerRef;
import com.sun.enterprise.config.serverbeans.Cluster;
import com.sun.enterprise.config.serverbeans.ConfigAPIHelper;
import com.sun.enterprise.config.serverbeans.ServerHelper;
import com.sun.enterprise.config.serverbeans.ClusterHelper;
import com.sun.enterprise.config.serverbeans.NodeAgentHelper;
import com.sun.enterprise.config.serverbeans.ElementProperty;
import com.sun.enterprise.config.ConfigException;
import com.sun.enterprise.config.ConfigContext;
import com.sun.enterprise.config.impl.ConfigContextImpl;
import com.sun.enterprise.admin.common.Status;
import com.sun.enterprise.admin.servermgmt.InstanceException;
import com.sun.enterprise.ee.admin.servermgmt.AgentException;
import com.sun.enterprise.admin.servermgmt.RuntimeStatus;
import com.sun.enterprise.admin.servermgmt.RuntimeStatusList;
import com.sun.enterprise.util.SystemPropertyConstants;
import com.sun.enterprise.util.i18n.StringManager;
import com.sun.enterprise.util.i18n.StringManagerBase;
import com.sun.enterprise.util.net.NetUtils;
import com.sun.enterprise.admin.target.TargetType;
import com.sun.enterprise.admin.target.Target;
import com.sun.enterprise.admin.target.TargetBuilder;
import com.sun.enterprise.admin.util.IAdminConstants;
import com.sun.enterprise.server.Constants;
import com.sun.enterprise.ee.admin.clientreg.InstanceRegistry;
import com.sun.enterprise.ee.admin.clientreg.NodeAgentRegistry;
import com.sun.enterprise.ee.admin.clientreg.MBeanServerConnectionInfo;
import com.sun.logging.ee.EELogDomains;
import com.sun.enterprise.ee.admin.servermgmt.EEInstancesManager;
import com.sun.enterprise.ee.admin.proxy.NodeAgentProxy;
import com.sun.enterprise.ee.admin.mbeanapi.NodeAgentMBean;
import com.sun.enterprise.ee.admin.proxy.InstanceProxy;
import com.sun.enterprise.ee.admin.mbeanapi.ServerRuntimeMBean;
import com.sun.enterprise.ee.admin.concurrent.Task;
import com.sun.enterprise.ee.admin.concurrent.Executor;
import com.sun.enterprise.addons.AddonFacade;
import java.rmi.RemoteException;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.Properties;
import java.util.Enumeration;
import java.util.ArrayList;
import java.io.File;
import javax.management.MBeanException;
//ISSUE: Do we really want to throws an InstanceException here as this will clients
//using this mbean to have our runtime; however we seem to be throwing our own
//exceptions everywhere else in the mbeans. The problem with MBeanException
//currently is that it masks the real exception (due to the fact that MBeanHelper
//does some bogus formatting on the exception.
public class ServersConfigBean extends ServersAndClustersBaseBean implements IAdminConstants
{
class GetRuntimeStatusTask extends Task {
private static final long TIMEOUT_IN_MILLIS = 30000;
private String _serverName;
private RuntimeStatus _status;
public GetRuntimeStatusTask(String serverName) {
super(TIMEOUT_IN_MILLIS);
_serverName = serverName;
_status = new RuntimeStatus(_serverName);
}
public RuntimeStatus getStatus() {
return _status;
}
public void run() {
try {
_status = getRuntimeStatus(_serverName);
} catch (InstanceException ex) {
StringManagerBase sm = StringManagerBase.getStringManager(
getLogger().getResourceBundleName());
getLogger().log(Level.WARNING,
sm.getString("eeadmin.listInstances.Exception", _serverName), ex);
_status = new RuntimeStatus(_serverName);
}
}
}
private static final StringManager _strMgr =
StringManager.getManager(ServersConfigBean.class);
private static final TargetType[] VALID_LIST_TYPES = new TargetType[] {
TargetType.DOMAIN, TargetType.CLUSTER, TargetType.SERVER, TargetType.NODE_AGENT};
private static final TargetType[] VALID_TYPES = new TargetType[] {
TargetType.SERVER};
public ServersConfigBean(ConfigContext configContext) {
super(configContext);
}
public void clearRuntimeStatus(String serverName) throws InstanceException
{
try {
ServerRuntimeMBean serverMBean = InstanceProxy.getInstanceProxy(serverName);
serverMBean.clearRuntimeStatus();
} catch (Exception ex) {
//Ignore any error indicating that the server is unreachable.
//FIXTHIS: We could expect the proxy to do this for us as it
//seems to be a common case.
if (InstanceProxy.isUnreachable(ex)) {
// do nothing
} else {
throw new InstanceException(ex);
}
}
}
public RuntimeStatus getRuntimeStatus(String serverName) throws InstanceException
{
try {
ServerRuntimeMBean serverMBean = InstanceProxy.getInstanceProxy(serverName);
return serverMBean.getRuntimeStatus();
} catch (Exception ex) {
//Ignore any error indicating that the server is unreachable.
//FIXTHIS: We could expect the proxy to do this for us as it
//seems to be a common case.
if (InstanceProxy.isUnreachable(ex)) {
return new RuntimeStatus(serverName);
} else {
throw new InstanceException(ex);
}
}
}
public boolean isRunning(String serverName) throws InstanceException
{
return getRuntimeStatus(serverName).isRunning();
}
/**
* Fetches the runtime status of the given server instances in parallel.
*/
public RuntimeStatusList getRuntimeStatus(Server[] servers)
{
return getRuntimeStatus(toStringArray(servers));
}
public RuntimeStatusList getRuntimeStatus(String[] servers)
{
GetRuntimeStatusTask[] tasks = new GetRuntimeStatusTask[servers.length];
for (int i = 0; i < servers.length; i++) {
tasks[i] = new GetRuntimeStatusTask(servers[i]);
}
Executor exec = new Executor(tasks);
exec.run();
RuntimeStatusList result = new RuntimeStatusList(servers.length);
for (int i = 0; i < servers.length; i++) {
result.add(tasks[i].getStatus());
}
return result;
}
public RuntimeStatusList getServerInstanceRuntimeStatus(String targetName)
throws InstanceException
{
final ConfigContext configContext = getConfigContext();
final StringManager stringMgr = StringManager.getManager(EEInstancesManager.class);
try {
final Target target = TargetBuilder.INSTANCE.createTarget(
DOMAIN_TARGET, VALID_LIST_TYPES, targetName, configContext);
final Server[] servers = target.getServers();
return getRuntimeStatus(servers);
} catch (Exception ex) {
throw getExceptionHandler().handleInstanceException(
ex, "eeadmin.listInstances.Exception", targetName);
}
}
/**
* Lists server instances.
*/
public String[] listServerInstancesAsString(
String targetName, boolean andStatus) throws InstanceException
{
final ConfigContext configContext = getConfigContext();
final StringManager stringMgr = StringManager.getManager(EEInstancesManager.class);
try {
final Target target = TargetBuilder.INSTANCE.createTarget(
DOMAIN_TARGET, VALID_LIST_TYPES, targetName, configContext);
final Server[] servers = target.getServers();
final int numServers = servers.length;
ArrayList result = new ArrayList();
String serverName = null;
int status;
RuntimeStatusList statusList = null;
if (andStatus) {
//Fetch the status of all the server instances in parallel
statusList = getRuntimeStatus(servers);
}
for (int i = 0; i < numServers; i++) {
serverName = servers[i].getName();
if (andStatus) {
//Do not display the DAS as a server instance
if (!ServerHelper.isDAS(configContext, serverName)) {
result.add(stringMgr.getString("listInstanceElement", serverName,
statusList.getStatus(i).toShortString()));
}
} else {
result.add(serverName);
}
}
return (String[])result.toArray(new String[result.size()]);
} catch (Exception ex) {
throw getExceptionHandler().handleInstanceException(
ex, "eeadmin.listInstances.Exception", targetName);
}
}
public String listDASServerInstanceAsString(boolean andStatus) throws InstanceException
{
final ConfigContext configContext = getConfigContext();
String dasName = null;
try {
final Server das = ServerHelper.getDAS(configContext);
dasName = das.getName();
return listServerInstancesAsString(dasName, andStatus)[0];
} catch (Exception ex) {
throw getExceptionHandler().handleInstanceException(
ex, "eeadmin.listInstances.Exception", dasName);
}
}
public String[] listUnclusteredServerInstancesAsString(
boolean andStatus, boolean excludeDASInstance)
throws InstanceException
{
final ConfigContext configContext = getConfigContext();
final StringManager stringMgr = StringManager.getManager(EEInstancesManager.class);
try {
final Server[] servers = ServerHelper.getUnclusteredServers(
configContext, excludeDASInstance);
ArrayList result = new ArrayList();
String serverName;
RuntimeStatusList statusList = null;
if (andStatus) {
//Fetch the status of all the server instances in parallel
statusList = getRuntimeStatus(servers);
}
for (int i = 0; i < servers.length; i++) {
serverName = servers[i].getName();
if (andStatus) {
result.add(stringMgr.getString("listInstanceElement", serverName,
statusList.getStatus(i).toShortString()));
} else {
result.add(serverName);
}
}
return (String[])result.toArray(new String[result.size()]);
} catch (Exception ex) {
throw getExceptionHandler().handleInstanceException(
ex, "eeadmin.listInstances.Exception", "unclustered instances");
}
}
/**
* Starts the specified server instance. This operation is invoked by the asadmin start-instance
* command.
*/
public void startServerInstance(String serverName) throws InstanceException
{
startServerInstance(serverName, null);
}
/**
* Starts the specified server instance. This operation is invoked by the asadmin start-instance
* when invoked with setenv option.
*/
public void startServerInstance(
String serverName, Properties props) throws InstanceException
{
try {
final ConfigContext configContext = getConfigContext();
//validate that the server exists and return its node agent
Target target = null;
try {
target = TargetBuilder.INSTANCE.createTarget(
VALID_TYPES, serverName, configContext);
} catch (Exception e) {
throw new InstanceException(
_strMgr.getString("noSuchInstance", serverName));
}
NodeAgent controller = target.getNodeAgents()[0];
String agentName = controller.getName();
// Call the Node Agent to start the server instance remotely
NodeAgentsConfigBean ncb = getNodeAgentsConfigBean();
if (ncb.isRunning(agentName)) {
if (isRunning(serverName)) {
// WBN January 2007 Issue 726
// This is now OFFICIALLY not an error. Log it and return
StringManagerBase sm = StringManagerBase.getStringManager(
getLogger().getResourceBundleName());
getLogger().log(Level.WARNING,
sm.getString("eeadmin.already.running", serverName));
return;
} else {
String installRoot = System.getProperty(
SystemPropertyConstants.INSTALL_ROOT_PROPERTY);
String instanceRoot = System.getProperty(
SystemPropertyConstants.INSTANCE_ROOT_PROPERTY);
NodeAgentMBean agentMBean = NodeAgentProxy.getNodeAgentProxy(agentName);
agentMBean.startInstance(serverName, props);
}
} else {
throw new InstanceException(_strMgr.getString("agentNotRunning",
agentName, serverName));
}
} catch (Exception ex) {
throw getExceptionHandler().handleInstanceException(
ex, "eeadmin.startServerInstance.Exception", serverName);
}
}
/**
* Stops the specified server instance. This operation is invoked by the asadmin stop-instance
* command.
*/
public void stopServerInstance(
String serverName, int timeout) throws InstanceException
{
stopServerInstance(serverName, true, timeout);
}
/**
* Stops the specified server instance. This operation is invoked by the asadmin stop-instance
* command.
*/
public void stopServerInstance(
String serverName) throws InstanceException
{
stopServerInstance(serverName, false, -1);
}
/**
* Stops the specified server instance. This operation is invoked by the asadmin stop-instance
* command.
*/
private void stopServerInstance(
String serverName, boolean forcekill, int timeout) throws InstanceException
{
try {
final ConfigContext configContext = getConfigContext();
//validate that the server exists and return its node agent
final Target target = TargetBuilder.INSTANCE.createTarget(
VALID_TYPES, serverName, configContext);
NodeAgent controller = target.getNodeAgents()[0];
String agentName = controller.getName();
// Call the Node Agent to stop the server instance remotely
NodeAgentsConfigBean ncb = getNodeAgentsConfigBean();
if (ncb.isRunning(agentName)) {
NodeAgentMBean agentMBean =
NodeAgentProxy.getNodeAgentProxy(agentName);
if (forcekill) {
agentMBean.stopInstance(serverName, timeout);
} else {
agentMBean.stopInstance(serverName);
}
} else throw new InstanceException(
_strMgr.getString("agentNotRunning", agentName, serverName));
} catch (Exception ex) {
throw getExceptionHandler().handleInstanceException(
ex, "eeadmin.stopServerInstance.Exception", serverName);
}
}
/**
* Restart server instance
*/
public void restartServerInstance(
String serverName) throws InstanceException
{
boolean restarted = false;
try {
// stop the instance only if it is running
if (isRunning(serverName)) {
stopServerInstance(serverName);
}
// start the instance only if it is stopped
if (! isRunning(serverName)) {
startServerInstance(serverName, null);
restarted = true;
}
// if none of the above are true then the restart is a failure
// throw appropriate exception
if (! restarted) {
throw new InstanceException(_strMgr.getString("serverCannotRestart",
serverName));
}
} catch (Exception ex) {
throw getExceptionHandler().handleInstanceException(
ex, "eeadmin.restartServerInstance.Exception", serverName);
}
}
private void deleteClusterReferences(String serverName)
throws ConfigException, InstanceException
{
//If the instance is clustered, the we must remove the server refs from
//the cluster.
final ConfigContext configContext = getConfigContext();
final Cluster cluster = ClusterHelper.getClusterForInstance(configContext, serverName);
final ServerRef ref = cluster.getServerRefByRef(serverName);
if (ref == null) {
throw new InstanceException(_strMgr.getString("clusterMissingServerRef",
cluster.getName(), serverName));
} else {
cluster.removeServerRef(ref, OVERWRITE);
}
}
/**
* Deletes the specified server instance. This operation is invoked by the asadmin delete-instance
* command.
*/
public void deleteServerInstance(String serverName) throws InstanceException
{
try {
final ConfigContext configContext = getConfigContext();
ConfigContextImpl.lock();
//validate that the server exists and return its node agent
final Target target = TargetBuilder.INSTANCE.createTarget(
VALID_TYPES, serverName, configContext);
final NodeAgent controller = target.getNodeAgents()[0];
//Remove the server instance
final Domain domain = ConfigAPIHelper.getDomainConfigBean(configContext);
final Servers servers = domain.getServers();
final Server server = target.getServers()[0];
boolean isReachable = true;
try {
ServerRuntimeMBean serverMBean = InstanceProxy.getInstanceProxy(serverName);
Status status = serverMBean.getRuntimeStatus().getStatus();
} catch (Exception ex) {
if (InstanceProxy.isUnreachable(ex)) {
isReachable = false;
}
}
if(isReachable)
throw new InstanceException(_strMgr.getString("serverIsNotStopped",
serverName));
if (ServerHelper.isServerStandAlone(configContext, serverName)) {
//If the server instance is stand-alone, we must remove its stand alone
//configuration after removing the server instance (since a configuration
//can only be deleted if it is unreferenced). Unfortunately, if
//this fails, we leave an unreferenced standalone configurtion.
String configName = server.getConfigRef();
// remove the server
servers.removeServer(server, OVERWRITE);
//remove the standalone configuration
//FIXTHIS: One issue is that the call below will result in a call to flushAll
//which is also called below. This must be taken into account when we
//figure out the notification story.
getConfigsConfigBean().deleteConfiguration(configName);
} else {
if (ServerHelper.isServerClustered(configContext, server)) {
//If the instance is clustered, the we must remove the server refs from
//the cluster.
deleteClusterReferences(serverName);
}
//Remove the server
servers.removeServer(server, OVERWRITE);
}
//Remove our connction to the server instance
InstanceRegistry.removeInstanceConnection(serverName);
//Notify the node agent that a new server has been deleted to it so it can
//resynchronize.
//FIXTHIS: We force persistence, clear any notifications, and update the
//Application server's config context explicitely. Until this is modelled
//as an event notification (TBD) we need this to happen before notifying or
//the Node Agent will not synchronize the correct data.
//QUESTION: What happens if an exception is thrown above (e.g. in addNodeAgent). How do
//we restore the admin config context to its previous (and unpersisted value)???
flushAll();
NodeAgentMBean agentMBean = NodeAgentProxy.getNodeAgentProxy(controller.getName());
agentMBean.synchronizeWithDAS();
} catch (Exception ex) {
throw getExceptionHandler().handleInstanceException(
ex, "eeadmin.deleteServerInstance.Exception", serverName);
} finally {
ConfigContextImpl.unlock();
}
}
private void addServerProperties(Server server, Properties props) throws ConfigException
{
//Set the server instances properties.
//FIXTHIS: Should we be setting properties of standalone instances, since
//the properties are already set in the stand alone configuration?
if (props != null) {
for (Enumeration e = props.propertyNames() ; e.hasMoreElements() ;) {
String name = (String)e.nextElement();
String value = (String)props.getProperty(name);
//Add the new property
if (value != null) {
//Remove the property if it already exists
SystemProperty sysProp = server.getSystemPropertyByName(name);
if (sysProp != null) {
server.removeSystemProperty(sysProp, OVERWRITE);
}
SystemProperty ep = new SystemProperty();
ep.setName(name);
ep.setValue(value);
server.addSystemProperty(ep, OVERWRITE);
}
}
}
}
protected void addApplicationReference(Object server, boolean enabled, String name,
String virtualServers) throws ConfigException
{
ApplicationRef ref = new ApplicationRef();
ref.setEnabled(enabled);
ref.setRef(name);
if (virtualServers != null) {
ref.setVirtualServers(virtualServers);
}
((Server)server).addApplicationRef(ref, OVERWRITE);
}
protected void addResourceReference(Object server, boolean enabled, String name)
throws ConfigException
{
ResourceRef ref = new ResourceRef();
ref.setEnabled(enabled);
ref.setRef(name);
((Server)server).addResourceRef(ref, OVERWRITE);
}
private void createStandAloneServerInstance(
Server server, Properties props) throws ConfigException, InstanceException
{
//Create the standalone configuration to be referenced by the server.
//FIXTHIS: One issue is that the call below will result in a call to flushAll
//which is also called below. This must be taken into account when we
//figure out the notification story.
String standaloneConfigName = getConfigsConfigBean().createStandAloneConfiguration(
server.getName(), props);
//Create the server instance referencing this new standalone configuration
createSharedServerInstance(standaloneConfigName, server, null);
}
private void createClusteredServerInstance(String clusterName,
Server server, Properties props) throws ConfigException
{
final ConfigContext configContext = getConfigContext();
//Add properties
addServerProperties(server, props);
//Get the configuration specified by configName and ensure that it exists
Cluster cluster = ClusterHelper.getClusterByName(configContext,
clusterName);
//The clustered server inherits its configuration from the cluster
server.setConfigRef(cluster.getConfigRef());
//Now add the new server instance
// to avoid Validator's complains
Domain domain = ConfigAPIHelper.getDomainConfigBean(configContext);
domain.getServers().addServer(server, OVERWRITE);
//Add the server to the cluster
ServerRef serverRef = new ServerRef();
serverRef.setRef(server.getName());
cluster.addServerRef(serverRef, OVERWRITE);
//The clustered server inherits its applications from the cluster
ApplicationRef[] appRefs = cluster.getApplicationRef();
for (int i = 0; i < appRefs.length; i++) {
addApplicationReference(server, appRefs[i].isEnabled(),
appRefs[i].getRef(), appRefs[i].getVirtualServers());
}
//The clustered server inherits its resources from the cluster
ResourceRef[] resRefs = cluster.getResourceRef();
ResourceRef resRef = null;
for (int i = 0; i < resRefs.length; i++) {
addResourceReference(server, resRefs[i].isEnabled(),
resRefs[i].getRef());
}
}
private void createSharedServerInstance(String configName,
Server server, Properties props) throws ConfigException, InstanceException
{
final ConfigContext configContext = getConfigContext();
//Get the configuration specified by configName and ensure that it exists
//and is valid
Config config = validateSharedConfiguration(configContext, configName);
server.setConfigRef(configName);
//Add properties
addServerProperties(server, props);
//Add system applications and resources
addSystemApplications(server);
addSystemResources(server);
//Finally add the new server instance
Domain domain = ConfigAPIHelper.getDomainConfigBean(configContext);
domain.getServers().addServer(server, OVERWRITE);
}
/**
* Choose non-conflicting port numbers. Warning the incoming portsInUse is side
* affected, so that the PortInUse elements are modified to contain the new port.
*/
private ArrayList pickNonConflictingPorts(Server server, ArrayList portsInUse)
throws ConfigException
{
Properties props = new Properties();
PortInUse portInUse;
int port;
//We only want to check for port conflicts if the node agent specified has rendezvous'd;
//otherwise, the host name on which it is residing is unknown (defaulting to "localhost").
//Before we can indicate whether a port is in use, we must know the valid host name.
final boolean isBoundToHost = getPortConflictCheckerConfigBean().isBoundToHost(server);
//Iterate through all the conflicting ports and pick new values.
for (int i = 0; i < portsInUse.size(); i++) {
portInUse = (PortInUse)portsInUse.get(i);
port = portInUse.getPort();
int newPort = 0;
if (isBoundToHost) {
newPort = NetUtils.getNextFreePort(portInUse.getHostName(), port);
} else {
newPort = port + 1;
}
//Keep track of the newly assigned and non-conflicting port. WARNING: we are
//side affecting the incoming ArrayList.
portInUse.setNewPort(newPort);
//Build the list of system properties to add to the server instance
props.put(portInUse.getPropertyName(), new Integer(newPort).toString());
}
addServerProperties(server, props);
return portsInUse;
}
/**
* Check to see if any of the ports defined as system properties are in use or conflict with
* other ports in domain.xml (i.e. with other server instances on the same machine -- with
* the same node agent). If there are conflicts, then we pick new port numbers.
*/
private ArrayList resolvePortConflicts(Server server, Properties newProps) throws Exception
{
PortConflictCheckerConfigBean portChecker = getPortConflictCheckerConfigBean();
String serverName = server.getName();
ArrayList portsInUse = null;
try {
//First check to see whether any of the user specified ports are in use, if
//so an exception must be thrown
if (newProps != null) {
portChecker.checkForPortConflicts(server, newProps, newProps, false);
}
int i = 0;
//We loop trying trying to incrementally assign new ports.
while (true) {
//Perform some sort of sanity check to ensure
//that the port numbers do not conflict for other servers on the same
//node agent. This is accomplished by comparing properties whose
//name contains "port" and whose value is numeric. NOTE: port validation
//is done after adding the server. For this reason, we must rollback the
//addition of the server if the port check fails.
try {
portChecker.checkForPortConflicts(server, null, false);
//stop when there are no port conflicts
break;
} catch (PortInUseException ex) {
if (i++ > 25) {
//We do not want to throw the PortInUseException since it is an internal
//class and protected.
throw new InstanceException(ex.getMessage());
}
//If there were port conflicts, then we pick unused ports.
ArrayList newPorts = pickNonConflictingPorts(server, ex.getConflictingPorts());
//Keep track of the initial list of conflicting ports. This is tricky
//since the list in the PortInUseException will shrink as conflicts
//are resolved.
if (portsInUse == null) {
portsInUse = newPorts;
} else {
//Now take all the newly resolved ports and apply them to the original list, updating
//only the new port.
for (int j = 0; j < newPorts.size(); j++) {
PortInUse newPort = (PortInUse)newPorts.get(j);
for (int k = 0; k < portsInUse.size(); k++) {
PortInUse port = (PortInUse)portsInUse.get(k);
if (port.getPropertyName().equals(newPort.getPropertyName())) {
port.setNewPort(newPort.getNewPort());
break;
}
}
}
}
}
}
} catch (Exception ex) {
try {
deleteServerInstance(serverName);
} catch (Exception ex2) {
//Log
StringManagerBase sm = StringManagerBase.getStringManager(getLogger().getResourceBundleName());
getLogger().log(Level.WARNING,
sm.getString("eeadmin.createServerInstance.Exception", serverName), ex2);
}
throw (ex);
}
return portsInUse;
}
/**
* Creates a new server instance. This operation is invoked by the asadmin create-instance
* command.
*/
public void createServerInstance(
String nodeAgentName, String serverName,
String configName, String clusterName, Properties props)
throws InstanceException, PortReplacedException
{
ArrayList conflictingPorts = null;
try {
final ConfigContext configContext = getConfigContext();
ConfigContextImpl.lock();
//validate name uniqueness
if (!ConfigAPIHelper.isNameUnique(configContext, serverName)) {
throw new InstanceException(_strMgr.getString("serverNameNotUnique",
serverName));
}
ConfigAPIHelper.checkLegalName(serverName);
// see if nodeagent exists, if not create an empty reference
Domain domain = ConfigAPIHelper.getDomainConfigBean(configContext);
NodeAgents agents=domain.getNodeAgents();
NodeAgent agent=agents.getNodeAgentByName(nodeAgentName);
if (agent == null) {
// create an implnodeagent reference
NodeAgentsConfigBean naMBean = getNodeAgentsConfigBean();
naMBean.createNodeAgentConfig(nodeAgentName);
}
//a configuration and cluster cannot both be specified
if (configName != null && clusterName != null) {
throw new InstanceException(_strMgr.getString("configAndClusterMutuallyExclusive"));
}
//Ensure that server specified by serverName does not already exist.
//Given that we've already checked for uniqueness earlier, this should never
//be the case, but we'll be extra safe here.
Servers servers = domain.getServers();
Server server = servers.getServerByName(serverName);
if (server != null) {
throw new InstanceException(_strMgr.getString("serverAlreadyExists",
serverName));
}
//Create the new server instance
server = new Server();
server.setNodeAgentRef(nodeAgentName);
server.setName(serverName);
if (configName != null) {
createSharedServerInstance(configName, server, props);
} else if (clusterName != null) {
createClusteredServerInstance(clusterName, server, props);
} else {
//FIXTHIS: One issue is that the call below will result in a call to flushAll
//which is also called below. This must be taken into account when we
//figure out the notification story.
createStandAloneServerInstance(server, props);
}
//Check for and resolve port conflicts. The list of port conflicts is maintained.
//This is called after creating the server. This is due to the fact that port
//conflicts are resolved and a marker PortReplacedException is thrown; however
//the server is still created.
conflictingPorts = resolvePortConflicts(server, props);
//Notify the node agent that a new server has been added to it so it can
//resynchronize.
//FIXTHIS: We force persistence, clear any notifications, and update the
//Application server's config context explicitely. Until this is modelled
//as an event notification (TBD) we need this to happen before notifying or
//the Node Agent will not synchronize the correct data.
//QUESTION: What happens if an exception is thrown above (e.g. in addNodeAgent). How do
//we restore the admin config context to its previous (and unpersisted value)???
flushAll();
NodeAgentMBean agentMBean = NodeAgentProxy.getNodeAgentProxy(nodeAgentName);
agentMBean.synchronizeWithDAS();
} catch (Exception ex) {
throw getExceptionHandler().handleInstanceException(
ex, "eeadmin.createServerInstance.Exception", serverName);
} finally {
ConfigContextImpl.unlock();
}
//Finally after all is said and done, we throw an exception if there were port
//conflicts.
if (conflictingPorts != null) {
throw new PortReplacedException(conflictingPorts);
}
}
private String[] toStringArray(Server[] sa)
{
int numServers = sa.length;
final String[] result = new String[numServers];
for (int i = 0; i < numServers; i++)
{
result[i] = sa[i].getName();
}
return result;
}
private PropertyConfigBean getPropertyConfigBean() {
return new PropertyConfigBean(getConfigContext());
}
private NodeAgentsConfigBean getNodeAgentsConfigBean()
{
return new NodeAgentsConfigBean(getConfigContext());
}
private ConfigsConfigBean getConfigsConfigBean()
{
return new ConfigsConfigBean(getConfigContext());
}
private PortConflictCheckerConfigBean getPortConflictCheckerConfigBean()
{
return new PortConflictCheckerConfigBean(getConfigContext());
}
}