ca.uqac.info.trace.generation.AmazonEcsGenerator.java Source code

Java tutorial

Introduction

Here is the source code for ca.uqac.info.trace.generation.AmazonEcsGenerator.java

Source

/******************************************************************************
  Event trace generator
  Copyright (C) 2012 Sylvain Halle
      
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation; either version 3 of the License, or
  (at your option) any later version.
      
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
      
  You should have received a copy of the GNU Lesser General Public License along
  with this program; if not, write to the Free Software Foundation, Inc.,
  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 ******************************************************************************/
package ca.uqac.info.trace.generation;

import java.util.*;

import org.apache.commons.cli.*;

import ca.uqac.info.trace.*;
import ca.uqac.info.util.RandomPicker;

import org.w3c.dom.*;

/**
 * Generate a random trace of Amazon ECS shopping cart requests and responses.
 * The constraints followed by the sequence of messages are detailed in:
 * <blockquote>
 * S. Hall, R. Villemaire. (2012). Constraint-Based Invocation of Stateful Web
 * Services: the Beep Store (Case Study). In
 * <cite>4th International Workshop on
 * Principles of Engineering Service-Oriented Systems (PESOS 2012)</cite>.
 * </blockquote>
 * @author Sylvain Hall
 *
 */
public class AmazonEcsGenerator extends TraceGenerator {
    /**
     * Intervals for all the aforementioned parameters
     */
    protected int m_maxCartSize = 10;
    protected int m_catalogSize = 10;
    protected int m_maxCarts = 1;
    protected int m_itemsPerOperation = 1;

    /**
     * Maximum price for items (we don't really care
     * about that)
     */
    protected static final int MAX_PRICE = 40;

    /**
     * Maximum quantity for items (we don't really care
     * about that)
     */
    protected static final int MAX_QTY = 10;

    /**
     * Actions one can take on the cart
     * @author Sylvain Hall
     *
     */
    protected enum Action {
        ITEM_SEARCH, CART_CREATE, CART_CLEAR, CART_ADD, CART_REMOVE, CART_EDIT
    };

    /**
     * Creates a trace generator with default settings
     */
    public AmazonEcsGenerator() {
        m_seed = 0;
        m_random = new Random(m_seed);
        m_minMessages = 1;
        m_maxMessages = 10;
    }

    /**
     * Sets the seed used by the internal random number generator.
     * Starting from the same seed and the same input parameters, successive
     * calls to generate will always generate the same sequence of
     * output traces.
     * @param s
     */
    public void setSeed(long s) {
        m_seed = s;
        m_random.setSeed(s);
    }

    @Override
    public EventTrace generate() {
        if (m_clockAsSeed)
            setSeed(System.currentTimeMillis());
        EventTrace trace = new EventTrace();
        Vector<Cart> carts = new Vector<Cart>();
        // Create random pool of items
        Vector<Item> items = new Vector<Item>();
        for (int i = 0; i < m_catalogSize; i++)
            items.add(new Item(i, m_random.nextInt(MAX_PRICE)));
        // We choose the number of messages to produce
        int n_messages = m_random.nextInt(m_maxMessages + 1 - m_minMessages) + m_minMessages;
        for (int i = 0; i < n_messages; i += 2) {
            Vector<Action> available_operations = new Vector<Action>();
            // Step 1: determine which operations are available
            available_operations.add(Action.ITEM_SEARCH);
            if (carts.size() < m_maxCarts)
                available_operations.add(Action.CART_CREATE);
            if (carts.size() > 0)
                available_operations.add(Action.CART_CLEAR);
            for (Cart c : carts) {
                if (c.size() < m_maxCartSize)
                    available_operations.add(Action.CART_ADD);
                if (c.size() > 0) {
                    available_operations.add(Action.CART_REMOVE);
                    available_operations.add(Action.CART_EDIT);
                }
            }
            // Step 2: pick an operation
            RandomPicker<Action> action_picker = new RandomPicker<Action>(m_random);
            Action operation = action_picker.pick(available_operations);
            // Step 3: create request and response
            switch (operation) {
            case ITEM_SEARCH:
                createItemSearch(trace, carts, items);
                break;
            case CART_CREATE:
                createCartCreate(trace, carts, items);
                break;
            case CART_ADD:
                createCartAdd(trace, carts, items);
                break;
            case CART_CLEAR:
                createCartClear(trace, carts, items);
                break;
            case CART_REMOVE:
                createCartRemove(trace, carts, items);
                break;
            case CART_EDIT:
                createCartEdit(trace, carts, items);
                break;
            }
        }
        return trace;
    }

    /**
     * Creates a Cart create request-response pair.
     * @param trace The trace to which the events will be added
     * @param carts The current set of shopping carts
     * @param items The current set of catalog items
     */
    private void createCartCreate(EventTrace trace, Vector<Cart> carts, Vector<Item> items) {
        Cart chosen_cart = new Cart();
        Vector<Item> items_to_add = new Vector<Item>();
        // Pick items to add to the cart
        RandomPicker<Item> item_picker = new RandomPicker<Item>(m_random);
        for (int i = 0; i < m_itemsPerOperation; i++) {
            int qty = 1;
            Item it = item_picker.pick(items);
            chosen_cart.put(it, qty);
            items_to_add.add(it);
        }
        // Add cart to list of carts
        carts.add(chosen_cart);
        { // Create the cart create request
            Node n = trace.getNode();
            n.appendChild(createKeyValue(trace, "SessionKey", "0"));
            n.appendChild(createKeyValue(trace, "Action", "CartCreate"));
            Node n_items = trace.createElement("Items");
            for (Item it : items_to_add) {
                Node n_item = trace.createElement("Item");
                n_item.appendChild(createKeyValue(trace, "ItemId", it.getId()));
                n_item.appendChild(createKeyValue(trace, "Quantity", 1));
                n_items.appendChild(n_item);
            }
            /*for (Item it : items_to_add)
            {
               n_items.appendChild(it.toNode(trace));
            }*/
            n.appendChild(n_items);
            trace.add(new Event(n));
        }
        { // Create the cart create response message
            Node n = trace.getNode();
            n.appendChild(createKeyValue(trace, "SessionKey", "0"));
            n.appendChild(createKeyValue(trace, "Action", "CartCreateResponse"));
            n.appendChild(createKeyValue(trace, "CartId", chosen_cart.getId()));
            n.appendChild(chosen_cart.toNode(trace));
            trace.add(new Event(n));
        }
    }

    /**
     * Creates a Cart add request-response pair.
     * @param trace The trace to which the events will be added
     * @param carts The current set of shopping carts
     * @param items The current set of catalog items
     */
    private void createCartAdd(EventTrace trace, Vector<Cart> carts, Vector<Item> items) {
        // Pick a cart to add to
        Vector<Cart> eligible_carts = new Vector<Cart>(carts);
        for (Cart c : carts)
            if (c.size() < m_maxCartSize)
                eligible_carts.add(c);
        RandomPicker<Cart> cart_picker = new RandomPicker<Cart>(m_random);
        Cart chosen_cart = cart_picker.pick(eligible_carts);
        // From that cart, pick an item to add
        Vector<Item> eligible_items = new Vector<Item>();
        for (Item it : items)
            if (!chosen_cart.containsKey(it))
                eligible_items.add(it);
        RandomPicker<Item> item_picker = new RandomPicker<Item>(m_random);
        Vector<Item> items_to_add = new Vector<Item>();
        for (int i = 0; i < m_itemsPerOperation; i++) {
            int qty = 1;
            Item it = item_picker.pick(eligible_items);
            chosen_cart.put(it, qty);
            items_to_add.add(it);
        }
        { // Create the cart add request
            Node n = trace.getNode();
            n.appendChild(createKeyValue(trace, "SessionKey", "0"));
            n.appendChild(createKeyValue(trace, "Action", "CartAdd"));
            n.appendChild(createKeyValue(trace, "CartId", chosen_cart.getId()));
            Node n_items = trace.createElement("Items");
            for (Item it : items_to_add) {
                Node n_item = trace.createElement("Item");
                n_item.appendChild(createKeyValue(trace, "ItemId", it.getId()));
                n_item.appendChild(createKeyValue(trace, "Quantity", 1));
                n_items.appendChild(n_item);
            }
            n.appendChild(n_items);
            trace.add(new Event(n));
        }
        { // Create the cart add response message
            Node n = trace.getNode();
            n.appendChild(createKeyValue(trace, "SessionKey", "0"));
            n.appendChild(createKeyValue(trace, "Action", "CartAddResponse"));
            n.appendChild(createKeyValue(trace, "CartId", chosen_cart.getId()));
            n.appendChild(chosen_cart.toNode(trace));
            trace.add(new Event(n));
        }
    }

    /**
     * Creates an Item Search request-response pair.
     * @param trace The trace to which the events will be added
     * @param carts The current set of shopping carts
     * @param items The current set of catalog items
     */
    private void createItemSearch(EventTrace trace, Vector<Cart> carts, Vector<Item> items) {
        { // Create the search request
            Node n = trace.getNode();
            n.appendChild(createKeyValue(trace, "SessionKey", "0"));
            n.appendChild(createKeyValue(trace, "Action", "ItemSearch"));
            n.appendChild(createKeyValue(trace, "Keyword", "dummy"));
            trace.add(new Event(n));
        }
        { // Create the search response
            int num_items = m_random.nextInt(m_maxCartSize);
            Node n = trace.getNode();
            n.appendChild(createKeyValue(trace, "SessionKey", "0"));
            n.appendChild(createKeyValue(trace, "Action", "ItemSearchResponse"));
            Node n_items = trace.createElement("Items");
            for (int i = 0; i < num_items; i++) {
                RandomPicker<Item> item_picker = new RandomPicker<Item>(m_random);
                Item cart_i = item_picker.pick(items);
                n_items.appendChild(cart_i.toNode(trace));
            }
            n.appendChild(n_items);
            trace.add(new Event(n));
        }
    }

    /**
     * Creates an cart clear request-response pair.
     * @param trace The trace to which the events will be added
     * @param carts The current set of shopping carts
     * @param items The current set of catalog items
     */
    private void createCartClear(EventTrace trace, Vector<Cart> carts, Vector<Item> items) {
        // Pick a cart to clear
        Vector<Cart> eligible_carts = new Vector<Cart>(carts);
        RandomPicker<Cart> cart_picker = new RandomPicker<Cart>(m_random);
        Cart chosen_cart = cart_picker.pick(eligible_carts);
        { // Create the cart clear message
            Node n = trace.getNode();
            n.appendChild(createKeyValue(trace, "SessionKey", "0"));
            n.appendChild(createKeyValue(trace, "Action", "CartClear"));
            n.appendChild(createKeyValue(trace, "CartId", chosen_cart.getId()));
            trace.add(new Event(n));
        }
        { // Create the cart clear response message
            Node n = trace.getNode();
            n.appendChild(createKeyValue(trace, "SessionKey", "0"));
            n.appendChild(createKeyValue(trace, "Action", "CartClearResponse"));
            n.appendChild(createKeyValue(trace, "CartId", chosen_cart.getId()));
            trace.add(new Event(n));
        }
    }

    /**
     * Creates an cart remove request-response pair.
     * @param trace The trace to which the events will be added
     * @param carts The current set of shopping carts
     * @param items The current set of catalog items
     */
    private void createCartRemove(EventTrace trace, Vector<Cart> carts, Vector<Item> items) {
        // Pick a cart to remove from
        Vector<Cart> eligible_carts = new Vector<Cart>();
        for (Cart c : carts)
            if (c.size() > 0)
                eligible_carts.add(c);
        RandomPicker<Cart> cart_picker = new RandomPicker<Cart>(m_random);
        Cart chosen_cart = cart_picker.pick(eligible_carts);
        // From that cart, pick an item to remove
        RandomPicker<Item> item_picker = new RandomPicker<Item>(m_random);
        Item chosen_item = item_picker.pick(items);
        { // Create the cart remove message
            Node n = trace.getNode();
            n.appendChild(createKeyValue(trace, "SessionKey", "0"));
            n.appendChild(createKeyValue(trace, "Action", "CartRemove"));
            n.appendChild(createKeyValue(trace, "CartId", chosen_cart.getId()));
            Node n_items = trace.createElement("Items");
            Node n_item = trace.createElement("Item");
            n_item.appendChild(createKeyValue(trace, "ItemId", chosen_item.getId()));
            n_items.appendChild(n_item);
            n.appendChild(n_items);
            trace.add(new Event(n));
        }
        // Remove item from cart
        chosen_cart.remove(chosen_item);
        { // Create the cart remove response message
            Node n = trace.getNode();
            n.appendChild(createKeyValue(trace, "SessionKey", "0"));
            n.appendChild(createKeyValue(trace, "Action", "CartRemoveResponse"));
            n.appendChild(createKeyValue(trace, "CartId", chosen_cart.getId()));
            n.appendChild(chosen_cart.toNode(trace));
            trace.add(new Event(n));
        }
    }

    private static Node createKeyValue(EventTrace trace, String key, String value) {
        Node n = trace.createElement(key);
        Node v = trace.createTextNode(value);
        n.appendChild(v);
        return n;
    }

    private static Node createKeyValue(EventTrace trace, String key, Integer value) {
        return createKeyValue(trace, key, value.toString());
    }

    private static Node createKeyValue(EventTrace trace, String key, int value) {
        return createKeyValue(trace, key, new Integer(value));
    }

    /**
     * Creates an cart edit request-response pair.
     * @param trace The trace to which the events will be added
     * @param carts The current set of shopping carts
     * @param items The current set of catalog items
     */
    private void createCartEdit(EventTrace trace, Vector<Cart> carts, Vector<Item> items) {
        // Pick a cart to edit
        Vector<Cart> eligible_carts = new Vector<Cart>();
        for (Cart c : carts)
            if (c.size() > 0)
                eligible_carts.add(c);
        RandomPicker<Cart> cart_picker = new RandomPicker<Cart>(m_random);
        Cart chosen_cart = cart_picker.pick(eligible_carts);
        // From that cart, pick an item to edit
        Item chosen_item = chosen_cart.pickItem();
        Integer new_qty = new Integer(m_random.nextInt(MAX_QTY));
        { // Create the cart edit message
            Node n = trace.getNode();
            n.appendChild(createKeyValue(trace, "SessionKey", "0"));
            n.appendChild(createKeyValue(trace, "Action", "CartEdit"));
            n.appendChild(createKeyValue(trace, "CartId", chosen_cart.getId()));
            Node n_items = trace.createElement("Items");
            Node n_item = trace.createElement("Item");
            n_item.appendChild(createKeyValue(trace, "ItemId", chosen_item.getId()));
            n_item.appendChild(createKeyValue(trace, "Quantity", new_qty));
            n_items.appendChild(n_item);
            n.appendChild(n_items);
            trace.add(new Event(n));
        }
        // Edit item in cart
        chosen_cart.put(chosen_item, new_qty);
        { // Create the cart edit response message
            Node n = trace.getNode();
            n.appendChild(createKeyValue(trace, "SessionKey", "0"));
            n.appendChild(createKeyValue(trace, "Action", "CartEditResponse"));
            n.appendChild(createKeyValue(trace, "CartId", chosen_cart.getId()));
            n.appendChild(chosen_cart.toNode(trace));
            trace.add(new Event(n));
        }
    }

    @Override
    public String getAppName() {
        return "Amazon ECS Shopping Cart Generator";
    }

    @SuppressWarnings("static-access")
    @Override
    public Options getCommandLineOptions() {
        Options options = new Options();
        Option opt;
        options.addOption("t", false, "Use system clock as random generator's seed (default: no)");
        opt = OptionBuilder.withArgName("x").hasArg()
                .withDescription("Set random generator's seed to x (default: 0)").create("s");
        options.addOption(opt);
        opt = OptionBuilder.withArgName("x").hasArg()
                .withDescription("Set maximum number of simultaneous shopping carts to x (default: 1)").create("C");
        options.addOption(opt);
        opt = OptionBuilder.withArgName("x").hasArg().withDescription("Set store's catalog size to x (default: 10)")
                .create("i");
        options.addOption(opt);
        opt = OptionBuilder.withArgName("x").hasArg()
                .withDescription("Set each cart's maximum size to x (default: 10)").create("k");
        options.addOption(opt);
        opt = OptionBuilder.withArgName("x").hasArg()
                .withDescription("Maximum number of messages to produce (default: 10)").create("N");
        options.addOption(opt);
        opt = OptionBuilder.withArgName("x").hasArg()
                .withDescription("Minimum number of messages to produce (default: 1)").create("n");
        options.addOption(opt);
        return options;
    }

    @Override
    public void initialize(CommandLine c_line) {
        if (c_line.hasOption("s"))
            setSeed(new Integer(c_line.getOptionValue("s")).intValue());
        if (c_line.hasOption("t"))
            m_clockAsSeed = true;
        if (c_line.hasOption("C"))
            m_maxCarts = new Integer(c_line.getOptionValue("C")).intValue();
        if (c_line.hasOption("n"))
            m_minMessages = new Integer(c_line.getOptionValue("n")).intValue();
        if (c_line.hasOption("N"))
            m_maxMessages = new Integer(c_line.getOptionValue("N")).intValue();
        if (c_line.hasOption("k"))
            m_maxCartSize = new Integer(c_line.getOptionValue("k")).intValue();
        if (c_line.hasOption("i"))
            m_catalogSize = new Integer(c_line.getOptionValue("i")).intValue();

    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        process(args, new AmazonEcsGenerator());
    }

    private int cart_id_count = 0;

    /**
     * Basic implementation of an Amazon shopping cart
     * @author Sylvain Hall
     *
     */
    private class Cart extends HashMap<Item, Integer> {
        /**
        * Mandatory UID (we don't care)
        */
        private static final long serialVersionUID = 1L;

        private int m_id = 0;

        private Random m_randomGen;

        public Cart() {
            super();
            m_id = cart_id_count++;
            m_randomGen = AmazonEcsGenerator.this.m_random;
        }

        /**
         * Randomly pick an item from the cart.
         * This method uses the random number generator from the container
         * classe (AmazonEcsGenerator).
         * @return
         */
        public Item pickItem() {
            assert size() > 0;
            Vector<Item> items = new Vector<Item>();
            items.addAll(super.keySet());
            int index = m_randomGen.nextInt(items.size());
            return items.elementAt(index);
        }

        public int getId() {
            return m_id;
        }

        public int hashCode() {
            return m_id;
        }

        public boolean equals(Object o) {
            if (o == null)
                return false;
            if (!(o instanceof Cart))
                return false;
            return equals((Cart) o);
        }

        public boolean equals(Cart c) {
            if (c == null)
                return false;
            return m_id == c.m_id;
        }

        public Node toNode(EventTrace t) {
            Node root = t.createElement("Items");
            for (Map.Entry<Item, Integer> en : super.entrySet()) {
                Item i = en.getKey();
                Integer qty = en.getValue();
                Node n_item = i.toNode(t);
                n_item.appendChild(createKeyValue(t, "Quantity", qty));
                root.appendChild(n_item);
            }
            return root;
        }
    }

    /**
     * Basic implementation of a cart item
     * @author Sylvain Hall
     *
     */
    private class Item {
        protected int m_id = 0;
        protected int m_price = 0;

        public Item(int id, int price) {
            super();
            m_id = id;
            m_price = price;
        }

        public int getId() {
            return m_id;
        }

        public int getPrice() {
            return m_price;
        }

        public int hashCode() {
            return m_id;
        }

        public boolean equals(Object o) {
            if (o == null)
                return false;
            if (!(o instanceof Item))
                return false;
            return equals((Item) o);
        }

        public boolean equals(Item c) {
            if (c == null)
                return false;
            return m_id == c.m_id;
        }

        public Node toNode(EventTrace t) {
            Node n_item = t.createElement("Item");
            n_item.appendChild(createKeyValue(t, "ItemId", getId()));
            n_item.appendChild(createKeyValue(t, "Price", getPrice()));
            return n_item;
        }
    }

}