Author: pferraro
Date: 2008-09-20 14:07:04 -0400 (Sat, 20 Sep 2008)
New Revision: 1874
Added:
trunk/mod_cluster/src/demo/
trunk/mod_cluster/src/demo/java/
trunk/mod_cluster/src/demo/java/org/
trunk/mod_cluster/src/demo/java/org/jboss/
trunk/mod_cluster/src/demo/java/org/jboss/modcluster/
trunk/mod_cluster/src/demo/java/org/jboss/modcluster/demo/
trunk/mod_cluster/src/demo/java/org/jboss/modcluster/demo/client/
trunk/mod_cluster/src/demo/java/org/jboss/modcluster/demo/client/ChartManager.java
trunk/mod_cluster/src/demo/java/org/jboss/modcluster/demo/client/ModClusterDemo.java
trunk/mod_cluster/src/demo/java/org/jboss/modcluster/demo/client/RequestDriver.java
Log:
Demo client from Brian
Added: trunk/mod_cluster/src/demo/java/org/jboss/modcluster/demo/client/ChartManager.java
===================================================================
--- trunk/mod_cluster/src/demo/java/org/jboss/modcluster/demo/client/ChartManager.java
(rev 0)
+++
trunk/mod_cluster/src/demo/java/org/jboss/modcluster/demo/client/ChartManager.java 2008-09-20
18:07:04 UTC (rev 1874)
@@ -0,0 +1,146 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This 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 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+
+package org.jboss.modcluster.demo.client;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+/**
+ * @author Brian Stansberry
+ *
+ */
+public class ChartManager
+{
+ private final Map<String, AtomicInteger> requestCounts;
+ private final Map<String, AtomicInteger> sessionCounts;
+ private final Map<String, Integer> lastRequestCounts = new HashMap<String,
Integer>();
+ private final Map<String, XYSeries> requestSeries = new HashMap<String,
XYSeries>();
+ private final Map<String, XYSeries> sessionSeries = new HashMap<String,
XYSeries>();
+ private final XYSeriesCollection requestSeriesCollection = new XYSeriesCollection();
+ private final XYSeriesCollection sessionSeriesCollection = new XYSeriesCollection();
+ private final JFreeChart requestBalancingChart;
+ private final JFreeChart sessionBalancingChart;
+ private long lastUpdateTime = 0;
+ private int seriesCount;
+
+ public ChartManager(Map<String, AtomicInteger> requestCounts,
+ Map<String, AtomicInteger> sessionCounts)
+ {
+ this.requestCounts = requestCounts;
+ this.sessionCounts = sessionCounts;
+
+ requestBalancingChart = ChartFactory.createXYLineChart("Request
Balancing", "Sample", "Requests / Second",
requestSeriesCollection, PlotOrientation.VERTICAL, true, true, false);
+ sessionBalancingChart = ChartFactory.createXYLineChart("Session
Balancing", "Sample", "Session Count", sessionSeriesCollection,
PlotOrientation.VERTICAL, true, true, false);
+
+ for (int i = 1; i < 9; i++)
+ {
+ String key = "cluster0" + i;
+ createRequestSeries(key);
+ createSessionSeries(key);
+ }
+ }
+
+ public JFreeChart getRequestBalancingChart()
+ {
+ return this.requestBalancingChart;
+ }
+
+ public JFreeChart getSessionBalancingChart()
+ {
+ return this.sessionBalancingChart;
+ }
+
+ public void start()
+ {
+ this.lastRequestCounts.clear();
+ this.lastUpdateTime = System.currentTimeMillis();
+ }
+
+ public void updateStats()
+ {
+ Integer xValue = new Integer(++seriesCount);
+
+ long now = System.currentTimeMillis();
+ long elapsed = (now - lastUpdateTime) / 1000;
+ this.lastUpdateTime = now;
+
+ for (Map.Entry<String, AtomicInteger> entry : requestCounts.entrySet())
+ {
+ String key = entry.getKey();
+ Integer current = new Integer(entry.getValue().get());
+ Integer last = lastRequestCounts.put(key, current);
+ if (last == null)
+ {
+ last = new Integer(0);
+ }
+
+ int perSec = (int) ((current.intValue() - last.intValue()) / elapsed);
+
+ XYSeries series = requestSeries.get(key);
+ if (series == null)
+ {
+ series = createRequestSeries(key);
+ }
+
+ series.add(xValue, new Integer(perSec));
+ }
+
+ for (Map.Entry<String, AtomicInteger> entry : sessionCounts.entrySet())
+ {
+ String key = entry.getKey();
+ XYSeries series = sessionSeries.get(key);
+ if (series == null)
+ {
+ series = createSessionSeries(key);
+ }
+
+ series.add(xValue, new Integer(entry.getValue().get()));
+ }
+ }
+
+ private XYSeries createSessionSeries(String key)
+ {
+ XYSeries series = new XYSeries(key);
+ series.setMaximumItemCount(20);
+ sessionSeries.put(key, series);
+ sessionSeriesCollection.addSeries(series);
+ return series;
+ }
+
+ private XYSeries createRequestSeries(String key)
+ {
+ XYSeries series = new XYSeries(key);
+ series.setMaximumItemCount(20);
+ requestSeries.put(key, series);
+ requestSeriesCollection.addSeries(series);
+ return series;
+ }
+
+}
Added:
trunk/mod_cluster/src/demo/java/org/jboss/modcluster/demo/client/ModClusterDemo.java
===================================================================
--- trunk/mod_cluster/src/demo/java/org/jboss/modcluster/demo/client/ModClusterDemo.java
(rev 0)
+++
trunk/mod_cluster/src/demo/java/org/jboss/modcluster/demo/client/ModClusterDemo.java 2008-09-20
18:07:04 UTC (rev 1874)
@@ -0,0 +1,450 @@
+package org.jboss.modcluster.demo.client;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.EventQueue;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+import javax.swing.JTextField;
+
+import org.jboss.modcluster.demo.client.RequestDriver.ClientStatus;
+import org.jfree.chart.ChartPanel;
+
+/**
+ * Client application for demonstrating load balancing with mod_cluster.
+ *
+ * @author Brian Stansberry
+ *
+ */
+public class ModClusterDemo
+{
+ private static final String DEFAULT_HOST_NAME =
"cluster10.qa.atl2.redhat.com:8888";
+ private static final String DEFAULT_REQUEST_URL = "web/index.jsp";
+ private static final String DEFAULT_DESTROY_URL = "web/destroy.jsp";
+ private static final int DEFAULT_NUM_THREADS = 200;
+ private static final int DEFAULT_SESSION_LIFE = 10;
+ private static final int DEFAULT_SLEEP_TIME = 0;
+ private static final int DEFAULT_STARTUP_TIME = 10;
+
+ private final RequestDriver requestDriver;
+ private final ChartManager chartManager;
+ private final Timer timer;
+ private TimerTask currentTask;
+ private JFrame frame;
+ private JTextField hostNameField;
+ private JTextField requestUrlField;
+ private JTextField destroyUrlField;
+ private JTextField numThreadsField;
+ private JTextField sessionLifeField;
+ private JTextField sleepTimeField;
+ private JTextField startupTimeField;
+ private JLabel totalClientsLabel;
+ private JLabel liveClientsLabel;
+ private JLabel failedClientsLabel;
+
+ /**
+ * Launch the application
+ * @param args
+ */
+ public static void main(String args[])
+ {
+ EventQueue.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ ModClusterDemo window = new ModClusterDemo();
+ window.frame.setVisible(true);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ /**
+ * Create the application
+ */
+ public ModClusterDemo()
+ {
+ this.requestDriver = new RequestDriver();
+ this.chartManager = new ChartManager(this.requestDriver.getRequestCounts(),
this.requestDriver.getSessionCounts());
+ this.timer = new Timer("ModClusterDemoTimer", false);
+
+ // Set up GUI
+ createContents();
+ }
+
+ /**
+ * Initialize the contents of the frame
+ */
+ private void createContents()
+ {
+ frame = new JFrame();
+ frame.setBounds(100, 100, 675, 422);
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.setTitle("Demo of mod-cluster");
+ frame.addWindowListener(new WindowAdapter()
+ {
+ @Override
+ public void windowClosed(WindowEvent e)
+ {
+ ModClusterDemo.this.stop();
+ ModClusterDemo.this.timer.cancel();
+ }
+ });
+
+ final JTabbedPane tabbedPane = new JTabbedPane();
+ frame.getContentPane().add(tabbedPane, BorderLayout.CENTER);
+
+ final JPanel controlPanel = new JPanel();
+ GridBagLayout gridBagLayout = new GridBagLayout();
+ gridBagLayout.columnWidths = new int[] {7,0,7,0,7,0,7,7,0,0,7};
+ gridBagLayout.rowHeights = new int[] {0,7,0,7,0,7,0,7, 0};
+ controlPanel.setLayout(gridBagLayout);
+ tabbedPane.addTab("Control", null, controlPanel, null);
+
+ final JPanel requestBalancingPanel = new
ChartPanel(this.chartManager.getRequestBalancingChart(), true);
+ tabbedPane.addTab("Request Balancing", null, requestBalancingPanel,
null);
+
+ final JPanel sessionBalancingPanel = new
ChartPanel(this.chartManager.getSessionBalancingChart(), true);
+ tabbedPane.addTab("Session Balancing", null, sessionBalancingPanel,
null);
+
+ // Control panel contents
+
+ JLabel label = new JLabel();
+ label.setText("Host name:");
+ GridBagConstraints gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 0;
+ gridBagConstraints.gridx = 1;
+ gridBagConstraints.weighty = 1;
+ gridBagConstraints.anchor = GridBagConstraints.SOUTH;
+ controlPanel.add(label, gridBagConstraints);
+
+ hostNameField = new JTextField();
+ hostNameField.setText(DEFAULT_HOST_NAME);
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 0;
+ gridBagConstraints.gridx = 3;
+ gridBagConstraints.gridwidth = 7;
+ gridBagConstraints.weightx = 1;
+ gridBagConstraints.weighty = 1;
+ gridBagConstraints.anchor = GridBagConstraints.SOUTH;
+ gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
+ controlPanel.add(hostNameField, gridBagConstraints);
+
+ label = new JLabel();
+ label.setText("Request URL:");
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 2;
+ gridBagConstraints.gridx = 1;
+ controlPanel.add(label, gridBagConstraints);
+
+ requestUrlField = new JTextField();
+ requestUrlField.setText(DEFAULT_REQUEST_URL);
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 2;
+ gridBagConstraints.gridx = 3;
+ gridBagConstraints.weightx = 1;
+ gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
+ controlPanel.add(requestUrlField, gridBagConstraints);
+
+ label = new JLabel();
+ label.setText("Destroy URL:");
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 2;
+ gridBagConstraints.gridx = 6;
+ controlPanel.add(label, gridBagConstraints);
+
+ destroyUrlField = new JTextField();
+ destroyUrlField.setText(DEFAULT_DESTROY_URL);
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 2;
+ gridBagConstraints.gridx = 9;
+ gridBagConstraints.weightx = 1;
+ gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
+ controlPanel.add(destroyUrlField, gridBagConstraints);
+
+ label = new JLabel();
+ label.setText("Num threads:");
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 4;
+ gridBagConstraints.gridx = 1;
+ controlPanel.add(label, gridBagConstraints);
+
+ numThreadsField = new JTextField();
+ numThreadsField.setText(String.valueOf(DEFAULT_NUM_THREADS));
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 4;
+ gridBagConstraints.gridx = 3;
+ gridBagConstraints.weightx = 1;
+ gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
+ controlPanel.add(numThreadsField, gridBagConstraints);
+
+ label = new JLabel();
+ label.setText("Session Life (s):");
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 4;
+ gridBagConstraints.gridx = 6;
+ controlPanel.add(label, gridBagConstraints);
+
+ sessionLifeField = new JTextField();
+ sessionLifeField.setText(String.valueOf(DEFAULT_SESSION_LIFE));
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 4;
+ gridBagConstraints.gridx = 9;
+ gridBagConstraints.weightx = 1;
+ gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
+ controlPanel.add(sessionLifeField, gridBagConstraints);
+
+ label = new JLabel();
+ label.setText("Sleep time (ms):");
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 6;
+ gridBagConstraints.gridx = 1;
+ controlPanel.add(label, gridBagConstraints);
+
+ sleepTimeField = new JTextField();
+ sleepTimeField.setText(String.valueOf(DEFAULT_SLEEP_TIME));
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 6;
+ gridBagConstraints.gridx = 3;
+ gridBagConstraints.weightx = 1;
+ gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
+ controlPanel.add(sleepTimeField, gridBagConstraints);
+
+ label = new JLabel();
+ label.setText("Startup time (s):");
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 6;
+ gridBagConstraints.gridx = 6;
+ controlPanel.add(label, gridBagConstraints);
+
+ startupTimeField = new JTextField();
+ startupTimeField.setText(String.valueOf(DEFAULT_STARTUP_TIME));
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 6;
+ gridBagConstraints.gridx = 9;
+ gridBagConstraints.weightx = 1;
+ gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
+ controlPanel.add(startupTimeField, gridBagConstraints);
+
+ JButton startButton = new JButton();
+ startButton.addActionListener(new ActionListener() {
+ public void actionPerformed(final ActionEvent e)
+ {
+ start();
+ }
+ });
+ startButton.setText("Start");
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 8;
+ gridBagConstraints.gridx = 1;
+ gridBagConstraints.weighty = 3;
+ gridBagConstraints.weightx = 1;
+ gridBagConstraints.gridwidth = 3;
+ gridBagConstraints.anchor = GridBagConstraints.EAST;
+ controlPanel.add(startButton, gridBagConstraints);
+
+ JButton stopButton = new JButton();
+ stopButton.addActionListener(new ActionListener() {
+ public void actionPerformed(final ActionEvent e)
+ {
+ stop();
+ }
+ });
+ stopButton.setText("Stop");
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 8;
+ gridBagConstraints.gridx = 6;
+ gridBagConstraints.weighty = 2;
+ gridBagConstraints.weightx = 1;
+ gridBagConstraints.gridwidth = 4;
+ gridBagConstraints.anchor = GridBagConstraints.WEST;
+ controlPanel.add(stopButton, gridBagConstraints);
+
+ JPanel statusPanel = new JPanel();
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 9;
+ gridBagConstraints.gridx = 1;
+ gridBagConstraints.weighty = 2;
+ gridBagConstraints.weightx = 1;
+ gridBagConstraints.gridwidth = 9;
+ gridBagConstraints.fill = GridBagConstraints.BOTH;
+ controlPanel.add(statusPanel, gridBagConstraints);
+
+ gridBagLayout = new GridBagLayout();
+ gridBagLayout.columnWidths = new int[] {0,0,7};
+ gridBagLayout.rowHeights = new int[] {0,7,0,0};
+ statusPanel.setLayout(gridBagLayout);
+
+ label = new JLabel();
+ label.setText("Client Status");
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 0;
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridwidth = 4;
+ gridBagConstraints.weightx = 1;
+ gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
+ statusPanel.add(label, gridBagConstraints);
+
+ label = new JLabel();
+ label.setText("Total clients:");
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 2;
+ gridBagConstraints.gridx = 1;
+ gridBagConstraints.anchor = GridBagConstraints.WEST;
+ statusPanel.add(label, gridBagConstraints);
+
+ totalClientsLabel = new JLabel();
+ totalClientsLabel.setText(String.valueOf("0"));
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 2;
+ gridBagConstraints.gridx = 3;
+ gridBagConstraints.anchor = GridBagConstraints.WEST;
+ statusPanel.add(totalClientsLabel, gridBagConstraints);
+
+ label = new JLabel();
+ label.setText("Live clients:");
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 3;
+ gridBagConstraints.gridx = 1;
+ gridBagConstraints.anchor = GridBagConstraints.WEST;
+ statusPanel.add(label, gridBagConstraints);
+
+ liveClientsLabel = new JLabel();
+ liveClientsLabel.setText(String.valueOf("0"));
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 3;
+ gridBagConstraints.gridx = 3;
+ gridBagConstraints.anchor = GridBagConstraints.WEST;
+ statusPanel.add(liveClientsLabel, gridBagConstraints);
+
+ label = new JLabel();
+ label.setText("Failed clients:");
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 4;
+ gridBagConstraints.gridx = 1;
+ gridBagConstraints.anchor = GridBagConstraints.WEST;
+ statusPanel.add(label, gridBagConstraints);
+
+ failedClientsLabel = new JLabel();
+ failedClientsLabel.setText(String.valueOf("0"));
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridy = 4;
+ gridBagConstraints.gridx = 3;
+ gridBagConstraints.anchor = GridBagConstraints.WEST;
+ statusPanel.add(failedClientsLabel, gridBagConstraints);
+
+ }
+
+ private void start()
+ {
+ // Extract the Control panel contents
+ String host = hostNameField.getText();
+ String request_url = requestUrlField.getText();
+ String destroy_url = destroyUrlField.getText();
+
+ String tmp = "http://" + host + "/";
+ URL requestURL, destroyURL;
+ try
+ {
+ requestURL = new URL(tmp + request_url);
+ destroyURL = new URL(tmp + destroy_url);
+ }
+ catch (MalformedURLException e)
+ {
+ e.printStackTrace(System.err);
+ return;
+ }
+
+ int num_threads = DEFAULT_NUM_THREADS;
+ String numT = numThreadsField.getText();
+ if (numT != null && numT.trim().length() > 0)
+ {
+ num_threads = Integer.parseInt(numT);
+ }
+
+ int session_life = DEFAULT_SESSION_LIFE;
+ String sessL = sessionLifeField.getText();
+ if (sessL != null && sessL.trim().length() > 0)
+ {
+ session_life = Integer.parseInt(sessL);
+ }
+
+ int sleep_time = DEFAULT_SLEEP_TIME;
+ String sleepT = sleepTimeField.getText();
+ if (sleepT != null && sleepT.trim().length() > 0)
+ {
+ sleep_time = Integer.parseInt(sleepT);
+ }
+
+ int startup_time = DEFAULT_STARTUP_TIME;
+ String startT = startupTimeField.getText();
+ if (startT != null && startT.trim().length() > 0)
+ {
+ startup_time = Integer.parseInt(startT);
+ }
+
+ // Start the components
+ this.currentTask = new GUIUpdateTimerTask();
+ this.chartManager.start();
+ this.timer.schedule(this.currentTask, 2000, 2000);
+ this.requestDriver.start(requestURL, destroyURL, num_threads, session_life,
sleep_time, startup_time);
+ }
+
+ private void stop()
+ {
+ if (this.currentTask != null)
+ {
+ this.currentTask.cancel();
+ }
+
+ this.requestDriver.stop();
+
+ // Update the client status panel
+ updateStatusPanel();
+ }
+
+ private void updateStatusPanel()
+ {
+ ClientStatus status = requestDriver.getClientStatus();
+ totalClientsLabel.setText(String.valueOf(status.clientCount));
+ liveClientsLabel.setText(String.valueOf(status.liveClientCount));
+ int failedCount = status.clientCount - status.successfulClientCount;
+ failedClientsLabel.setText(String.valueOf(failedCount));
+ failedClientsLabel.setForeground(failedCount == 0 ? Color.BLACK : Color.RED);
+ }
+
+ private class GUIUpdateTimerTask extends TimerTask
+ {
+ @Override
+ public void run()
+ {
+ // Update the chart
+ chartManager.updateStats();
+
+ // Update the client status panel
+ updateStatusPanel();
+ }
+
+ }
+
+}
Added:
trunk/mod_cluster/src/demo/java/org/jboss/modcluster/demo/client/RequestDriver.java
===================================================================
--- trunk/mod_cluster/src/demo/java/org/jboss/modcluster/demo/client/RequestDriver.java
(rev 0)
+++
trunk/mod_cluster/src/demo/java/org/jboss/modcluster/demo/client/RequestDriver.java 2008-09-20
18:07:04 UTC (rev 1874)
@@ -0,0 +1,415 @@
+package org.jboss.modcluster.demo.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @author Brian Stansberry
+ * @version $Id: PerfTest2.java 68892 2008-01-11 14:39:07Z bela(a)jboss.com $
+ */
+public class RequestDriver
+{
+ private Client[] clients;
+
+ private static final String NODE_HEADER = "X-ClusterNode";
+
+ private final ConcurrentMap<String, AtomicInteger> requestCounts = new
ConcurrentHashMap<String, AtomicInteger>();
+ private final ConcurrentMap<String, AtomicInteger> sessionCounts = new
ConcurrentHashMap<String, AtomicInteger>();
+
+ private final AtomicBoolean stopped = new AtomicBoolean(false);
+ private Thread startThread;
+
+ public void start(final URL request_url, final URL destroy_url, int num_threads, final
int session_life,
+ final int sleep_time, int startup_time)
+ {
+ this.requestCounts.clear();
+ this.sessionCounts.clear();
+
+
+ this.clients = new Client[num_threads];
+
+ this.stopped.set(false);
+ final int startupPause = startup_time > 0 ? (startup_time * 1000 / num_threads)
: 0;
+
+ System.out.println("Starting " + num_threads + " clients");
+ Runnable r = new Runnable()
+ {
+ public void run()
+ {
+ for (int i = 0; i < clients.length & !stopped.get(); i++)
+ {
+ Client client = new Client(request_url, destroy_url, session_life,
sleep_time, requestCounts, sessionCounts, stopped);
+ clients[i] = client;
+ client.start();
+ try
+ {
+ Thread.sleep(startupPause);
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace(System.err);
+ return;
+ }
+ }
+
+ }
+ };
+
+ this.startThread = new Thread(r, "RequestDriverStartThread");
+ this.startThread.start();
+ }
+
+ public void stop()
+ {
+ // Stop creating new clients (if we still are)
+ this.stopped.set(true);
+ if (this.startThread != null && this.startThread.isAlive())
+ {
+ try { this.startThread.join(2000); } catch (InterruptedException e) {}
+
+ if (this.startThread.isAlive())
+ {
+ this.startThread.interrupt();
+ }
+ }
+
+ // Stop the clients we've created
+ if (this.clients != null)
+ {
+ for (Client client : this.clients)
+ {
+ if (client != null && client.isAlive())
+ {
+ client.terminate();
+ }
+ }
+ }
+ }
+
+ public ConcurrentMap<String, AtomicInteger> getRequestCounts()
+ {
+ return requestCounts;
+ }
+
+ public ConcurrentMap<String, AtomicInteger> getSessionCounts()
+ {
+ return sessionCounts;
+ }
+
+ public ClientStatus getClientStatus()
+ {
+ ClientStatus result = new ClientStatus();
+ if (this.clients != null)
+ {
+ for (Client client : this.clients)
+ {
+ if (client != null)
+ {
+ result.clientCount++;
+ if (client.isAlive())
+ {
+ result.liveClientCount++;
+ }
+ if (client.isSuccessful())
+ {
+ result.successfulClientCount++;
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ public static class ClientStatus
+ {
+ public int clientCount;
+ public int liveClientCount;
+ public int successfulClientCount;
+ }
+
+ private static class Client extends Thread
+ {
+ private final URL request_url, destroy_url;
+
+ private int successful_reads = 0, failed_reads = 0;
+
+ private final long sessionLife;
+
+ private final long sleepTime;
+
+ private long start = 0, stop = 0;
+
+ private boolean successful = true;
+
+ private final byte[] buffer = new byte[1024];
+
+ private String cookie = null;
+
+ private String lastHandler = null;
+
+ private final AtomicBoolean stopped;
+
+ private final ConcurrentMap<String, AtomicInteger> requests;
+
+ private final ConcurrentMap<String, AtomicInteger> sessions;
+
+ private Client(URL request_url, URL destroy_url, long sessionLife, int sleepTime,
+ ConcurrentMap<String, AtomicInteger> requests, ConcurrentMap<String,
AtomicInteger> sessions,
+ AtomicBoolean stopped)
+ {
+ this.request_url = request_url;
+ this.destroy_url = destroy_url;
+ this.sessionLife = sessionLife * 1000;
+ this.sleepTime = sleepTime;
+ this.requests = requests;
+ this.sessions = sessions;
+ this.stopped = stopped;
+ // Don't keep the VM alive
+ setDaemon(false);
+ }
+
+ public void run()
+ {
+ try
+ {
+ start = System.currentTimeMillis();
+ loop();
+ }
+ catch (Exception e)
+ {
+ error("failure", e);
+ successful = false;
+ }
+ finally
+ {
+ stop = System.currentTimeMillis();
+ try
+ {
+ if (cookie != null)
+ {
+ executeRequest(destroy_url, false);
+ }
+ }
+ catch (IOException e)
+ {
+ }
+ }
+ }
+
+ public int getFailedReads()
+ {
+ return failed_reads;
+ }
+
+ public int getSuccessfulReads()
+ {
+ return successful_reads;
+ }
+
+ public long getTime()
+ {
+ return stop - start;
+ }
+
+ public boolean isSuccessful()
+ {
+ return successful;
+ }
+
+ private void terminate()
+ {
+ if (this.isAlive())
+ {
+ try
+ {
+ this.join(5000);
+ if (this.isAlive())
+ {
+ this.interrupt();
+ }
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+ }
+
+ private void loop() throws IOException
+ {
+ int rc;
+
+ while (!stopped.get())
+ {
+ long sessionStart = System.currentTimeMillis();
+ long elapsed = 0;
+ boolean failed = false;
+ while ((elapsed < sessionLife) && !stopped.get())
+ {
+ try
+ {
+ Thread.sleep(sleepTime);
+ }
+ catch (InterruptedException e)
+ {
+ break;
+ }
+
+ rc = executeRequest(request_url, false);
+ if (rc == 200)
+ {
+ successful_reads++;
+ elapsed = System.currentTimeMillis() - sessionStart;
+ }
+ else
+ {
+ failed_reads++;
+ failed = true;
+ break;
+ }
+ }
+
+ if (!failed) // if we failed, executeRequest destroyed session for us
+ {
+ executeRequest(destroy_url, true);
+ }
+ }
+ }
+
+ private int executeRequest(URL url, boolean destroySession) throws IOException
+ {
+ InputStream input = null;
+ HttpURLConnection conn = null;
+ try
+ {
+ conn = (HttpURLConnection) url.openConnection(); // not yet connected
+ if (cookie != null)
+ conn.setRequestProperty("Cookie", cookie);
+
+ input = conn.getInputStream(); // NOW it is connected
+ while (input.read(buffer) > 0)
+ {
+ ;
+ }
+ input.close(); // discard data
+
+ String handlerNode = conn.getHeaderField(NODE_HEADER);
+ String tmp_cookie = conn.getHeaderField("set-cookie");
+ if (tmp_cookie != null && cookie == null)
+ {
+ // New session
+ cookie = tmp_cookie;
+ modifyCount(handlerNode, sessions, true);
+ }
+
+ modifyCount(handlerNode, requests, true);
+
+ // If we failed over, decrement the previous handler's count
+ if (!handlerNode.equals(lastHandler))
+ {
+ modifyCount(lastHandler, sessions, false);
+ }
+
+ int rc = conn.getResponseCode();
+ if ((rc != 200 || destroySession) && cookie != null)
+ {
+ cookie = null;
+ lastHandler = null;
+ modifyCount(handlerNode, sessions, false);
+ }
+ else if (cookie != null)
+ {
+ // Track this handler so we can decrement the session
+ // count in case of failover or error
+ lastHandler = handlerNode;
+ }
+
+ return rc;
+ }
+ catch (Exception e)
+ {
+ // Decrement the session count for whoever last handled a request
+ cookie = null;
+ modifyCount(lastHandler, sessions, false);
+ lastHandler = null;
+
+ if (e instanceof IOException)
+ throw (IOException) e;
+ else
+ throw (RuntimeException) e;
+ }
+ finally
+ {
+ if (conn != null)
+ conn.disconnect();
+ }
+ }
+
+ private static void modifyCount(String handlerNode, ConcurrentMap<String,
AtomicInteger> map, boolean increment)
+ {
+ if (handlerNode != null)
+ {
+ AtomicInteger count = map.get(handlerNode);
+ if (count == null)
+ {
+ count = new AtomicInteger();
+ AtomicInteger existing = map.putIfAbsent(handlerNode, count);
+ if (existing != null)
+ {
+ count = existing;
+ }
+ }
+
+ if (increment)
+ count.incrementAndGet();
+ else
+ count.decrementAndGet();
+ }
+ }
+
+ private static void error(String msg, Throwable th)
+ {
+ String tmp = "[thread-" + Thread.currentThread().getId() + "]:
" + msg;
+ if (th != null)
+ {
+ tmp += ", ex: " + th + "\n";
+ StringWriter writer = new StringWriter();
+ PrintWriter pw = new PrintWriter(writer);
+ th.printStackTrace(pw);
+ pw.flush();
+ tmp += writer.toString();
+ }
+ System.err.println(tmp);
+ }
+ }
+
+ // private static class MyCookieHandler extends CookieHandler {
+ // Map<URI,List<String>> cookies=new
HashMap<URI,List<String>>();
+ //
+ // public Map<String, List<String>> get(URI uri, Map<String,
List<String>> requestHeaders) throws IOException {
+ // Map<String,List<String>> map=new
HashMap<String,List<String>>();
+ // List<String> list=cookies.get(uri);
+ // if(list == null) {
+ // list=new LinkedList<String>();
+ // cookies.put(uri, list);
+ // }
+ // map.put("cookie", list);
+ // return Collections.unmodifiableMap(map);
+ // }
+ //
+ // public void put(URI uri, Map<String, List<String>>
responseHeaders) throws IOException {
+ // List<String> list=responseHeaders.get("Set-Cookie");
+ // if(list != null) {
+ // cookies.get(uri).addAll(list);
+ // }
+ // }
+ // }
+
+}
\ No newline at end of file