Package org.jdesktop.wonderland.testharness.master

Source Code of org.jdesktop.wonderland.testharness.master.SimpleTestDirector$FixedMaximumSlaveAllocator

/**
* Project Wonderland
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., All Rights Reserved
*
* Redistributions in source code form must reproduce the above
* copyright and this condition.
*
* The contents of this file are subject to the GNU General Public
* License, Version 2 (the "License"); you may not use this file
* except in compliance with the License. A copy of the License is
* available at http://www.opensource.org/licenses/gpl-license.php.
*
* Sun designates this particular file as subject to the "Classpath"
* exception as provided by Sun in the License file that accompanied
* this code.
*/
package org.jdesktop.wonderland.testharness.master;

import com.jme.math.Vector3f;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jdesktop.wonderland.testharness.common.ClientLoginRequest;
import org.jdesktop.wonderland.testharness.common.TestReply;
import org.jdesktop.wonderland.testharness.manager.common.CommsHandler;
import org.jdesktop.wonderland.testharness.manager.common.ManagerMessage;
import org.jdesktop.wonderland.testharness.manager.common.SimpleTestDirectorMessage;
import org.jdesktop.wonderland.testharness.manager.common.SimpleTestDirectorMessage.UserActionType;
import org.jdesktop.wonderland.testharness.master.SlaveConnection.SlaveConnectionListener;

/**
*
* @author paulby
*/
public class SimpleTestDirector implements TestDirector {

    private ArrayList<SlaveInfo> slaves = new ArrayList();
    private final HashMap<String, User> users = new LinkedHashMap();

    private String audioFile = null;

    private Logger logger = Logger.getLogger(SimpleTestDirector.class.getName());
   
    private int targetUsers = 1;
    private int slaveCount = 0; // Slaves currently in use by this director

    private static final String USER_MANAGER_PROP = "usermanager";
    private static final String USER_MANAGER_DEFAULT =
            "org.jdesktop.wonderland.testharness.master.GroupUserManagerImpl";
    private final UserManager userManager;

    private SlaveAllocator allocator = new RoundRobinSlaveAllocator();

    private CommsHandler commsHandler;
   
    public SimpleTestDirector(CommsHandler commsHandler, Properties props) {
        this.commsHandler = commsHandler;

        // create the user manager
        String umClass = props.getProperty(USER_MANAGER_PROP, USER_MANAGER_DEFAULT);
        userManager = createObject(umClass, UserManager.class);
        System.out.println("Initialized user manager: " + umClass + " " + userManager);
        userManager.initialize(props);

        audioFile = props.getProperty("slave.audio.file");

        commsHandler.addMessageListener(SimpleTestDirectorMessage.class, new CommsHandler.MessageListener() {

            public void messageReceived(ManagerMessage msg) {
                assert(msg instanceof SimpleTestDirectorMessage);
                SimpleTestDirectorMessage message = (SimpleTestDirectorMessage) msg;
               
                System.err.println("TestDirector received "+message.getMessageType());
               
                switch(message.getMessageType()) {
                    case REQUEST_STATUS :
                        sendUIUpdate();
                        String[] usernames = new String[users.size()];
                        UserActionType[] currentActions = new UserActionType[users.size()];
                        int i=0;
                        for(User u : users.values()) {
                            usernames[i] = u.getUsername();
                            currentActions[i] = u.getCurrentAction();
                            i++;
                        }
                        sendUIMessage(SimpleTestDirectorMessage.newUserListMessage(usernames, currentActions));
                        break;
                    case USER_COUNT :
                        targetUsers = message.getDesiredUserCount();
                        adjustUsers();
                        sendUIUpdate();
                        break;
                    case CHANGE_ALLOCATOR:
                        changeAllocator(message.getAllocatorName(),
                                        message.getProperties());
                        break;
                    case USER_ACTION_CHANGE_REQUEST :
                        User user = users.get(message.getUsername());
                        if (user!=null) {
                            userManager.changeUserAction(user, message.getUserAction());
                        }
                        break;
                    default :
                        System.err.println("Unexepected message type "+message.getMessageType());
                }

            }
        });
    }

    private <T> T createObject(String name, Class<T> clazz) {
        try {
            Class<T> create = (Class<T>) Class.forName(name);
            return create.newInstance();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    private void sendUIUpdate() {
        try {
            commsHandler.send(SimpleTestDirectorMessage.newUIUpdate(users.size(), targetUsers));
        } catch (IOException ex) {
            Logger.getLogger(SimpleTestDirector.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void sendUIMessage(SimpleTestDirectorMessage msg) {
        try {
            commsHandler.send(msg);
        } catch (IOException ex) {
            Logger.getLogger(SimpleTestDirector.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void changeAllocator(String allocatorName,
                                 Properties props)
    {
        SlaveAllocator out = null;

        if (allocatorName.equalsIgnoreCase("RoundRobin")) {
            out = new RoundRobinSlaveAllocator();
        } else if (allocatorName.equalsIgnoreCase("Fixed")) {
            out = new FixedMaximumSlaveAllocator();
        } else {
            Logger.getLogger(SimpleTestDirector.class.getName()).warning("Unknown allocator " +
                                                                         allocatorName);
            return;
        }

        out.configure(props);
        allocator = out;
    }

    private void adjustUsers() {
        System.err.println("adjustUsers "+targetUsers+"  "+users.size());
        // add users if necessary
        while (targetUsers > users.size() && addUser()) {
            // do nothing -- addUser() did the actual work
            try { Thread.sleep(500); } catch (InterruptedException ie) {}
        }

        // remove users if necessary
        while (targetUsers < users.size() && removeUser()) {
            // do nothing -- removeUser() did the actual work
            try { Thread.sleep(500); } catch (InterruptedException ie) {}
        }
    }

    private boolean addUser() {
        synchronized(users) {
            SlaveInfo slave = allocator.findSlave();
            System.err.println("ADDING USER TO SLAVE "+slave);
            if (slave == null) {
                return false;
            }

            User user = createUser(slave);
            slave.add(user);
            sendUIMessage(SimpleTestDirectorMessage.newUserAddedMessage(user.getUsername(), true));
        }
        return true;
    }

    private boolean removeUser() {
        synchronized(users) {
            User firstUser = users.values().iterator().next();
            firstUser.disconnect();
            destroyUser(firstUser);
       
            SlaveInfo slave = findSlave(firstUser);
            if (slave != null) {
                slave.remove(firstUser);
            }
        }


        return true;
    }

    /**
     * Find the slave containing the given user, or return null if no
     * slave contains the current user.
     * @param user the user to look for
     * @return the slave containing the given user
     */
    private SlaveInfo findSlave(User user) {
        for (SlaveInfo slave : slaves) {
            if (slave.contains(user)) {
                return slave;
            }
        }
       
        return null;
    }

    public boolean slaveJoined(SlaveConnection slaveConnection) {
        SlaveInfo slaveInfo = new SlaveInfo(slaveConnection);
        slaves.add(slaveInfo);
        slaveCount++;

        adjustUsers();
        sendUIUpdate();

        return true; // We used the slave so return true
    }
   
    private User createUser(SlaveInfo slaveInfo) {
        UserContextImpl context = new UserContextImpl(slaveInfo);
        User user = userManager.createUser(UsernameManager.getUniqueUsername(), context);
        users.put(user.getUsername(), user);
        String serverURL = MasterMain.getMaster().getSgsServerName();
       
        Properties props = new Properties();
        props.setProperty("serverURL", serverURL);
//        ClientLoginRequest lr = new ClientLoginRequest("client3D.Client3DSim", props,
//                                           user.getUsername());
        props.setProperty("testharness.actorPort", Integer.toString(context.getActorPort()));

        if (audioFile != null) {
            props.setProperty("slave.audio.file", audioFile);
        }

        ClientLoginRequest lr = new ClientLoginRequest("webstart.WebstartClientWrapper", props,
                                           user.getUsername());
        System.err.println("Send login request "+user.getUsername());
        slaveInfo.getConnection().send(lr);

        return user;
    }

    private void destroyUser(User user) {
        userManager.destroyUser(user);
        users.remove(user.getUsername());

        System.err.println("Removing " + user.getUsername());
        sendUIMessage(SimpleTestDirectorMessage.newUserAddedMessage(user.getUsername(), false));
    }
   
    class UserContextImpl implements UserContext {
        private SlaveInfo slaveInfo;
        private int actorPort;

        public UserContextImpl(SlaveInfo slaveInfo) {
            this.slaveInfo = slaveInfo;
            this.actorPort = slaveInfo.getNextActorPort();
        }

        public SlaveConnection getConnection() {
            return slaveInfo.getConnection();
        }

        private int getActorPort() {
            return actorPort;
        }

        public void sendUIMessage(SimpleTestDirectorMessage msg) {
            SimpleTestDirector.this.sendUIMessage(msg);
        }

        public void cleanup() {
            slaveInfo.freeActorPort(actorPort);
        }
    }
   
    class SlaveInfo extends ArrayList<User> implements SlaveConnectionListener, SlaveConnection.TestReplyListener {
        private SlaveConnection slaveConnection;

        private final LinkedList<Integer> freeActorPorts = new LinkedList<Integer>();
        private int nextActorPort = 15432;
       
        public SlaveInfo(SlaveConnection slaveConnection) {
            this.slaveConnection = slaveConnection;

            slaveConnection.addConnectionListener(this);
            slaveConnection.addReplyListener(this);
        }
       
        public SlaveConnection getConnection() {
            return slaveConnection;
        }

        public int getUserCount() {
            return size();
        }

        public void disconnected(SlaveConnection connection) {
            slaves.remove(this);
            slaveCount--;

            for (User user : this) {
                user.getContext().cleanup();
                destroyUser(user);
            }

            sendUIUpdate();
        }

        public int getNextActorPort() {
            synchronized(freeActorPorts) {
                Integer port = freeActorPorts.peekFirst();
                if (port!=null) {
                    freeActorPorts.removeFirst();
                    return port.intValue();
                }

                return nextActorPort++;
            }
        }

        /**
         * Return the specified port to the free list, so it can be reused
         * @param port
         */
        public void freeActorPort(int port) {
            synchronized(freeActorPorts) {
                freeActorPorts.add(port);
            }
        }

        public void received(TestReply reply) {
            System.err.println("SlaveInfo received "+reply);
            User u = users.get(reply.getUsername());
            if (u==null) {
                logger.warning("Reply for unknown user "+reply.getUsername());
            } else {
                u.processReply(reply);
            }
        }
    }

    interface SlaveAllocator {
        /** configure with the given properties */
        public void configure(Properties props);

        /** find the next available slave */
        public SlaveInfo findSlave();
    }

    class RoundRobinSlaveAllocator implements SlaveAllocator {
        public void configure(Properties props) {
            return;
        }

        public SlaveInfo findSlave() {
            if (slaves.isEmpty()) {
                return null;
            }

            // first, go through and find the maximum number of
            // users on any one slave
            int max = 0;
            for (SlaveInfo slave : slaves) {
                if (slave.size() > max) {
                    max = slave.size();
                }
            }

            // now find the the first slave with fewer than max
            // clients
            SlaveInfo out = null;
            for (SlaveInfo slave : slaves) {
                if (slave.size() < max) {
                    out = slave;
                    break;
                }
            }

            // no luck -- just pick the first
            if (out == null) {
                out = slaves.get(0);
            }
           
            // all set
            return out;
        }
    }

    class FixedMaximumSlaveAllocator implements SlaveAllocator {
        private int max = 10;

        public void configure(Properties props) {
            if (props.containsKey("max")) {
                max = Integer.parseInt(props.getProperty("max"));
            }
        }

        public SlaveInfo findSlave() {
            for (SlaveInfo slave : slaves) {
                if (slave.size() < max) {
                    return slave;
                }
            }

            return null;
        }
    }
}
TOP

Related Classes of org.jdesktop.wonderland.testharness.master.SimpleTestDirector$FixedMaximumSlaveAllocator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.