package javacodebook.xml.transport.jms.pubSub;

import java.io.*;
import javax.jms.*;
import javax.naming.*;

/**
 * Die XMLTopicSubscriber-Klasse benutzt eine Session, um sich für den Empfang von
 * XML-Dokumente in Form von TextMessages zu subskribieren.  Dabei werden XML-Dokumente als
 * reine Text-Dokumente behandelt. Das Parsen und eventuelle Validierung muss getrennt
 * geschehen.
 */
public class XMLTopicSubscriber {

  private static final String USAGE =
      "\nBenutzerhinweis: javacodebook.xml.transport.jms.pubSub.XMLTopicSubscriber " +
      "<topic>\n\nwobei\n\n<topic>\nddas Thema ist, das subskribiert werden soll";

  /**
   * An die main-Methode muss der Parameter <topic> übergeben werden.
   * In der Methode wird ein XMLTopicSubscriber-Objekt instanziiert, es wird der Kommandozeilen-Parameter
   * ausgelesen und die receiveMessages()-Methode mit dem Parameter <topic> aufgerufen.
   * Diese Methode empfängt solange Nachrichten bis entweder 'a' oder 'A' in die Standardeingabe
   * eingegeben wird und mit <return> bestätigt wird.
   */
  public static void main(String args[]) {
    XMLTopicSubscriber xMLTopicSubscriber = new XMLTopicSubscriber();
    String topic = "not set";

    if (args.length != 1) {
      System.out.println(getUsage());
      System.exit(1);
    }
    else {
      topic = args[0];
    }
    xMLTopicSubscriber.receiveMessages(topic);
  }

  public void receiveMessages(String topicString) {
    Context jndiContext = null;
    TopicConnectionFactory topicConnectionFactory = null;
    TopicConnection topicConnection = null;
    TopicSession topicSession = null;
    Topic topic = null;
    TopicSubscriber topicSubscriber = null;
    XMLListener xmlListener = null;

    System.out.println("Das subskribierte Thema lautet: " + topicString);

    // Erzeugung eines neuen JNDI API InitialContext Objektes
    try {
      jndiContext = new InitialContext();
    }
    catch (NamingException e) {
      System.out.println("Es konnte kein JNDI API Kontext erstellt werden: " +
                         e.toString());
      System.exit(1);
    }

    // Look-up der Connection-Factory und des Topics. Falls eines der Objekte nicht
    // gefunden wird, soll die Anwendung verlassen werden.
    try {
      topicConnectionFactory = (TopicConnectionFactory) jndiContext.lookup(
          "TopicConnectionFactory");
      topic = (Topic) jndiContext.lookup(topicString);
    }
    catch (NamingException e) {
      System.out.println("JNDI API lookup verfehlt: " +
                         e.toString() +
          "\n\nentweder die TopicConnectionFactory oder die Warteschlange namens " +
          topicString +
          " ist nicht beim Namensdienst registriert");
      System.exit(1);
    }

    try {

      // Eine neue Connection wird erzeugt.
      topicConnection = topicConnectionFactory.createTopicConnection();

      // Über die Connection wird ein TopicSession-Objekt erzeugt. Dadurch dass true übergeben wird,
      // ist die Verbindung transaktional - bei false wäre sie das nicht. Als 2. Parameter wir 0 übergeben um
      // anzudeuten, dass er bei transaktionalen TopicSessions keine Rolle spielt.  Falls keine transaktionale
      // TopicSession erzeugt wird, muss der 2. Parameter einer der Werte:
      //  * Session.AUTO_ACKNOWLEDGE;
      //  * Session.CLIENT_ACKNOWLEDGE;
      //  * Session.DUPS_OK_ACKNOWLEDGE;
      // sein.
      topicSession = topicConnection.createTopicSession(true, 0);

      // Erzeugung eines TopicSubscriber-Objektes über das TopicSession-Objekt
      topicSubscriber = topicSession.createSubscriber(topic);

      // ein neues XMLListener-Objekt wird erzeugt welches das MessageListener-Interface
      // implementiert
      xmlListener = new XMLListener();

      // Das XMLListener-Object wird beim TopicSubscriber-Object registriert.
      topicSubscriber.setMessageListener(xmlListener);

      // Bevor die Connection benutzt werden kann, muss sie gestartet werden
      topicConnection.start();

      System.out.println(
          "Zum Beenden der Anwendung bitte 'a' oder 'A' für abbrechen eingeben " +
          "dann bitte mit <return> bestätigen");
      InputStreamReader inputStreamReader = new InputStreamReader(System.in);
      char answer = 0;
      while (! ( (answer == 'a') || (answer == 'A'))) {
        try {
          answer = (char) inputStreamReader.read();
        }
        catch (IOException e) {
          System.out.println("Ausnahmezustand beim Lesen der Standardeingabe: "
                             + e.toString());
        }
      }

      // Da die TopicSession transaktional ist, muss die commit()-Methode aufgerufen werden um
      // Locks von den Nachrichten zu entfernen, die über diese TopicSession veröffentlicht wurden.
      try {
        topicSession.commit();
      }

      // Falls die Transaktion von Seiten des JMS-Providers nicht 'commited' werden konnte, wird eine JMSException geworfen, die
      // auch vom Typ der Unterklassen TransactionRolledBackException oder IllegalStateException sein kann.
      // Es empfielt sich, in solchen Fällen die empfangene Nachricht nicht weiter zu verarbeiten, da sie von dem Provider noch
      // mal ausgeliefert werden wird und somit eine doppelte Verarbeitung stattfinden würde.
      catch (JMSException e) {
        System.out.println("Ausnahmefall eingetreten:  " + e);
      }
    }
    catch (JMSException e) {
      System.out.println("Ausnahmefall eingetreten: " + e.toString());
    }
    finally {
      if (topicConnection != null) {
        try {

          // Die Connection wird geschlossen.
          topicConnection.close();
        }
        catch (JMSException e) {}
      }
    }
  }

  public static String getUsage() {
    return USAGE;
  }
}
