
package examples.jms.drawdemo;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.net.*;
import java.applet.*;
import java.util.*;
import javax.jms.*;
import javax.naming.*;

/**
 * This example is graphical illustration of some key concepts of the
 * JMS publish/subscribe model. With it, you can see the behavior of
 * transacted sessions, persistent messages, and durable subscribers.
 * <p>
 * Configure the server:
 * <ol>
 * <li>
 * If you want to try the persistent and durable subscriber features, 
 * make sure you have defined a JMS connection pool in your 
 * <tt>weblogic.properties</tt> file as described in <a href="../Package-examples.jms.html#about">
 * About this package</a>.
 * </ol>
 * <p>
 * Build the example:
 * <ol>
 * <li>
 * Set up your development shell, as described in 
 * <a href="../../../techstart/environment.html">Setting up 
 * your environment</a>.
 * <li>
 * Compile the file in this directory as shown in this examples for Windows NT:
 * <pre>  $<font face="Courier New"><b> javac -d %CLIENT_CLASSES% JMSDrawDemo.java</b></font></pre>
 * </ol>
 * <p>
 * Run the example:
 * <ol>
 * <li>
 * Start the WebLogic Server in a new command shell.
 * <li>
 * Start the JMSDrawDemo application in two or more development shells with 
 * the following command. Do not forget to set up new development shells, as described in 
 * <a href="../../../techstart/environment.html">Setting up 
 * your environment</a>.
 *( <pre>  $<font face="Courier New"><b> java examples.jms.drawdemo.JMSDrawDemo t3://<i>hostname</i>:<i>port</i> [<i>DurableSubID</i>]</b></font></pre>
 * where:
 * <dl>
 * <dt><i><font face="Courier New" size = -1><b>hostname</b></font></i>
 * <dd>Host name of the WebLogic Server
 * <dt><i><font face="Courier New" size = -1><b>port</b></font></i>
 * <dd>Port where the WebLogic Server is listening for connections
 * (<font face="Courier New" size=-1>weblogic.system.ListenPort</font>). 
 * <dt><i><font face="Courier New" size = -1><b>DurableSubID</b></font></i>
 * <dd>Include the Durable Subscriber ID to try out
 * persistence with Durable Subscribers. Each instance of the
 * application must have a unique Durable Subscriber ID.
 * </dl>
 * </ol>
 * <p> 
 * Each JMSDrawDemo instance displays a canvas. A click of the
 * mouse on a canvas publishes the mouse coordinates in a message to
 * the JMS topic. When other instances of the application receive a
 * message from the Topic, they display an icon at the specified
 * coordinates. As you click on one canvas, the others duplicate  
 * the arrangement of icons.
 *
 * <p> 
 * The JMSDrawDemo application has buttons and checkboxes that
 * send commands or or change various parameters. If you click the 
 * Clear button, the application sends a <tt>clearScreen</tt> message
 * to the Topic. When the message is delivered, all instances of the 
 * application clear their canvasses.  
 *
 * <p> 
 * Check the Transacted checkbox to turn on "transacted" mode.
 * When the transacted mode is on, JMS does not send messages until
 * you click the Commit button. Or click the Rollback button to roll
 * back the transaction.  To view a commit graphically, click several
 * times on the canvas, then click the Commit button.  Other canvasses do
 * not update until you commit.
 *
 * <p> 
 * Persistent messages are of most interest to durable
 * subscribers.  By running this example with an (optional) durable
 * subscriber name as the second argument, a durable subscription is
 * established.  Check the Persistent checkbox to enable persistent
 * JMS messages.  Persistent messages sent when the subscriber is not
 * active will be delivered when the subscriber reattaches, even after
 * rebooting the WebLogic Server.
 *
 * @author Copyright (c) 1999-2000 by BEA Systems, Inc. All Rights Reserved.  
 */
public class JMSDrawDemo extends JPanel
  implements MouseListener,  MessageListener, ActionListener
{

  // CONSTANTS
  public final static String JNDI_FACTORY="weblogic.jndi.WLInitialContextFactory";
  public final static String JMS_FACTORY="javax.jms.TopicConnectionFactory";
  public final static String TOPIC="javax.jms.exampleTopic";
  public final static String IMAGE="/weblogic/graphics/truck32x32.gif";


  // JMS MEMBER DATA
  private TopicConnectionFactory connectionFactory;
  private TopicConnection connection;
  private TopicSession session, sessionTX;
  private TopicPublisher publisher, publisherTX;
  private TopicSubscriber subscriber;
  private Topic topic;
  private Message msg;
  private boolean isTransacted;

  // UI MEMBER DATA
  private Image image;
  private boolean imageLoaded;
  private int imageWidth, imageHeight;
  private Vector points = new Vector();
  private Vector pointsSent = new Vector();
  private Image offScreenImage;
  private Graphics offScreenGraphics;
  private JButton commitButton, rollbackButton, clearButton, quitButton;
  private JCheckBox transactedCheckBox, persistentCheckBox;
  private Dimension d;


  /*******************************************************************
   * CONSTRUCTOR  -  Initialize the user interface & JMS objects
   *******************************************************************/

  public JMSDrawDemo(JFrame frame, String url, String durableSubscriberID) 
       throws NamingException, JMSException
  {
    frame.getContentPane().add(this, "Center");
    frame.getContentPane().add(makeControlPanel(), "South");
    image = loadImage(IMAGE);
    initJMS(url, durableSubscriberID);
    addMouseListener(this);
  }


  /*******************************************************************
   * Create all the necessary objects for sending & receiving JMS messages
   *******************************************************************/

  private void initJMS(String url, String durableSubscriberID)
       throws NamingException, JMSException
  {
    boolean noLocal = false; // do not suppress messages from self.

    InitialContext ctx = getInitialContext(url);

    connectionFactory = (TopicConnectionFactory) ctx.lookup(JMS_FACTORY);
    connection = connectionFactory.createTopicConnection();
    connection.setClientID("JMSDrawDemo");
    session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
    sessionTX = connection.createTopicSession(true, Session.AUTO_ACKNOWLEDGE);
    try {
      topic = (Topic) ctx.lookup(TOPIC);
    } catch (NamingException ne) {
      topic = session.createTopic(TOPIC);
      ctx.bind(TOPIC, topic);
    }
    publisher = session.createPublisher(topic);
    publisherTX = sessionTX.createPublisher(topic);

    if (durableSubscriberID == null) {
      subscriber = session.createSubscriber(topic, "TRUE", noLocal);
    } else {
      subscriber = session.createDurableSubscriber(topic, durableSubscriberID, "TRUE", noLocal);
    }
    subscriber.setMessageListener(this);
    msg = session.createMessage();
    connection.start();
  }



  /*******************************************************************
   *  Publish a JMS message with X/Y coordinates
   *******************************************************************/

  private void publishPoint(int x, int y)
  {
    try {
      msg.setIntProperty("x", x);
      msg.setIntProperty("y", y);
      pointsSent.addElement(new Point(x, y));
      if (isTransacted) {
        publisherTX.publish(msg, deliveryMode(), 5, 0);
      } else {
        publisher.publish(msg, deliveryMode(), 5, 0);
      }
    } catch (JMSException jmse) {
      jmse.printStackTrace();
    }
  }


  /*******************************************************************
   *  Publish a JMS "clearScreen" message
   *******************************************************************/

  private void publishClear() 
  {
    try {
      Message clearMsg = session.createMessage();
      clearMsg.setStringProperty("command", "clearScreen");
      publisher.publish(clearMsg, deliveryMode(), 5, 0);
    } catch (JMSException jmse) {
      jmse.printStackTrace();
    }
  }


  /*******************************************************************
   *  Receive a JMS message with X/Y coordinates, or a "clearScreen" message
   *******************************************************************/

  public void onMessage(Message msg)
  {
    try {
      String command = msg.getStringProperty("command");
      if (command != null && command.equals("clearScreen")) {
        clearScreen();
      } else {
        int x = msg.getIntProperty("x");
        int y = msg.getIntProperty("y");
        Point p = new Point(x,y);
        updateImage(p);
      }
    } catch (JMSException e) {
      e.printStackTrace();
    }
  }


  /*******************************************************************
   *  Close JMS objects, and exit
   *******************************************************************/

  private void exit()
  {
    try {
      publisher.close();
      publisherTX.close();
      subscriber.close();
      session.close();
      sessionTX.close();
      connection.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
    System.exit(0);
  }




  /*******************************************************************
   *  Make the User Interface
   *******************************************************************/

  private JPanel makeControlPanel() 
  {
    JPanel controlPanel = new JPanel();

    persistentCheckBox = new JCheckBox("Persistent");
    persistentCheckBox.addActionListener(this);
    controlPanel.add(persistentCheckBox);

    transactedCheckBox = new JCheckBox("Transacted");
    transactedCheckBox.addActionListener(this);
    controlPanel.add(transactedCheckBox);

    commitButton = new JButton("Commit");
    commitButton.setEnabled(false);
    commitButton.addActionListener(this);
    controlPanel.add(commitButton);

    rollbackButton = new JButton("Rollback");
    rollbackButton.setEnabled(false);
    rollbackButton.addActionListener(this);
    controlPanel.add(rollbackButton);

    clearButton = new JButton("Clear");
    clearButton.addActionListener(this);
    controlPanel.add(clearButton);

    quitButton = new JButton("Quit");
    quitButton.addActionListener(this);
    controlPanel.add(quitButton);

    return controlPanel;
  }


  /*******************************************************************
   *  Paint the user interface.
   *******************************************************************/

  public void paint(Graphics g) {
    g.setColor(Color.black);

    d = getSize();

    if (offScreenImage==null || 
        d.width != offScreenImage.getWidth(null) || 
        d.height != offScreenImage.getHeight(null)) {
      offScreenImage = createImage(d.width, d.height);
      offScreenGraphics = offScreenImage.getGraphics();
      Enumeration pts = points.elements();
      while(pts.hasMoreElements()) {
        Point pt = (Point) pts.nextElement();
        offScreenGraphics.drawImage(image, ((pt.x * d.width) / 1000) - (imageWidth/2), 
                                    ((pt.y * d.height) / 1000) -(imageHeight/2), this);
      }

      pts = pointsSent.elements();
      while(pts.hasMoreElements()) {
        Point pt = (Point) pts.nextElement();
        offScreenGraphics.setColor(Color.darkGray);
        offScreenGraphics.fillOval(((pt.x * d.width) / 1000)-5, ((pt.y * d.height) / 1000)-5, 10, 10);
      }
    }

    g.drawImage(offScreenImage, 0, 0, this);

  }

  public Dimension getPreferredSize()
  {
    return new Dimension(300,300);
  }


  /*******************************************************************
   *  Respond to JMS messages
   *******************************************************************/
  private void updateImage(Point pt)
  {
    points.addElement(pt);
    pointsSent.removeElement(pt);
    if (offScreenGraphics != null) {
      offScreenGraphics.drawImage(image, ((pt.x * d.width) / 1000) - (imageWidth/2), 
                                  ((pt.y * d.height) / 1000) -(imageHeight/2), this);
    }
    repaint();
  }



  /*******************************************************************
   *   Mouse Listener - respond to Mouse Events
   *******************************************************************/

  public void mousePressed(MouseEvent e) {}
  public void mouseClicked(MouseEvent e) {}
  public void mouseEntered(MouseEvent e) {}
  public void mouseExited(MouseEvent e) {}
  /**
   * The user has clicked in the canvas.  Send an event with the
   * mouse coordinates (as a scaled percentage of the screen size)
   */
  public void mouseReleased(MouseEvent e) 
  {
    int x = (e.getX() * 1000) / d.width;
    int y = (e.getY() * 1000) / d.height;

    if (isTransacted) {
      commitButton.setEnabled(true);
      rollbackButton.setEnabled(true);
    }
    offScreenGraphics.setColor(Color.darkGray);
    offScreenGraphics.fillOval(e.getX() - 5, e.getY()-5, 10, 10);
    publishPoint(x, y);
    repaint();
  }

  private void setTransacted(boolean transacted)
  {
    if (transacted != isTransacted) {
      if (! transacted) {
        doRollback();
      }
      isTransacted = transacted;
    }
  }


  /*******************************************************************
   *   ActionListener - Respond to button presses
   *******************************************************************/

  public void actionPerformed(ActionEvent ev) 
  {
    if (ev.getSource() == transactedCheckBox) {
      setTransacted(transactedCheckBox.isSelected());
      return;
    }
    if (ev.getSource() == commitButton) {
      doCommit();
      return;
    }
    if (ev.getSource() == rollbackButton) {
      doRollback();
      return;
    }
    if (ev.getSource() == clearButton) {
      publishClear();
      return;
    }
    if (ev.getSource() == quitButton) {
      exit();
    }
  }

  private void doCommit() 
  {
    try {
      sessionTX.commit();
      commitButton.setEnabled(false);
      rollbackButton.setEnabled(false);
      repaint();
    } catch (JMSException jmse) {
      jmse.printStackTrace();
    }
  }

  private void doRollback() 
  {
    try {
      sessionTX.rollback();
      pointsSent.removeAllElements();
      commitButton.setEnabled(false);
      rollbackButton.setEnabled(false);
      offScreenImage = null;
      repaint();
    } catch (JMSException jmse) {
      jmse.printStackTrace();
    }
  }


  private void clearScreen() 
  {
    if (isTransacted) {
      doRollback();
    }
    pointsSent.removeAllElements();
    points.removeAllElements();
    offScreenImage = null;
    repaint();
  }


  private int deliveryMode() 
  {
    return persistentCheckBox.isSelected()
      ? DeliveryMode.PERSISTENT
      : DeliveryMode.NON_PERSISTENT;
  }


  /*******************************************************************
   *  Image Loading Utilities
   *******************************************************************/
  public Image loadImage(String resourceName)
  {
    try {
      java.net.URL url = getClass().getResource(resourceName);
      java.awt.Toolkit tk = java.awt.Toolkit.getDefaultToolkit();
      image = tk.createImage((java.awt.image.ImageProducer) url.getContent());
      synchronized(this) {
        imageWidth = image.getWidth(this);
        imageHeight = image.getHeight(this);
        if (imageWidth <= 0 || imageHeight <= 0) {
          imageLoaded = false;
          while(!imageLoaded) {
            wait();
          }
        }
        imageWidth = image.getWidth(this);
        imageHeight = image.getHeight(this);
      }
      return image;
    } catch(Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  // ImageObserver Interface
  public boolean imageUpdate(Image image, int infoFlags,
                             int x, int y, int width, int height)
  {
    if(image == this.image &&
       (infoFlags & ImageObserver.ALLBITS) == ImageObserver.ALLBITS) {
      synchronized(this) {
        imageLoaded = true;
        notify();
      }
      return false;
    }
    return true;
  }

  

  /*******************************************************************
   *  MAIN
   *******************************************************************/
  public static void main(String argv [])
  {
    String url = null;
    String durableSubscriberID = null;
    String frameTitle = "JMSDrawDemo";

    if (argv.length > 0) {
      url = argv[0];
    } else {
      System.out.println("Usage: java examples.jms.drawdemo.JMSDrawDemo <url> [DurableSubscriberID]");
      System.exit(1);
    }

    if (argv.length > 1) {
      durableSubscriberID = argv[1];
      frameTitle += " ("+durableSubscriberID + ")";
    }

    final JFrame frame = new JFrame(frameTitle);
    frame.setForeground(Color.black);
    frame.setBackground(Color.lightGray);
    frame.getContentPane().setLayout(new BorderLayout());
    
    try {
      final JMSDrawDemo demo = 
        new JMSDrawDemo(frame, url, durableSubscriberID);

      WindowListener l = new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          demo.exit();
        }
      };
      frame.addWindowListener(l);
    } catch (NamingException ne) {
      System.out.println("Error attaching to server, url=" + url);
      System.exit(0);
    } catch (JMSException jmse) {
      jmse.printStackTrace();
      System.exit(0);
    }

    frame.pack();
    frame.setVisible(true);
  }

  // Return a JNDI Initial Context

  private static InitialContext getInitialContext(String url)
       throws NamingException
  {
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
    env.put(Context.PROVIDER_URL, url);
    return new InitialContext(env);
  }

}




