V2

Here is what you get when you add the second instance to a cluster

C:\WINDOWS>asadmin create-instance --cluster c1 --nodeagent vaio i2
Using 38,081 for HTTP_LISTENER_PORT.
Using 38,182 for HTTP_SSL_LISTENER_PORT.
Using 33,821 for IIOP_SSL_LISTENER_PORT.
Using 37,677 for JMS_PROVIDER_PORT.
Using 33,701 for IIOP_LISTENER_PORT.
Using 38,687 for JMX_SYSTEM_CONNECTOR_PORT.
Using 33,921 for IIOP_SSL_MUTUALAUTH_PORT.
Command create-instance executed successfully.

Wow! This was super-hard to find in V2 code.

Also the main class is PortConflictCheckerConfigBean
Over FIVE HUNDRED lines of code. This is not going to be easy!

admin/mbeans
com.sun.enterprise.ee.admin.configbeans.ServersConfigBean

/**
     * 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();
            //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 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;
    }

V2 Algorithm

if(there are user-specified port numbers AND any are non-number values or out of range)

Unknown macro: { FATAL();} if(internal duplicate port numbers in the one server 9e.g. HTTP and SSL are same number) FATAL(); if(server isn't running -- check that every port is free at host}

Old URL (read-only):
New Page:
https://wikis.sun.com/display/glassfish/AutomaticPortAssignment