package dataqueue;

 

import java.beans.PropertyChangeListener;

import java.beans.PropertyChangeSupport;

import java.io.IOException;

import java.io.UnsupportedEncodingException;

 

import com.ibm.as400.access.AS400;

import com.ibm.as400.access.AS400SecurityException;

import com.ibm.as400.access.ConnectionDroppedException;

import com.ibm.as400.access.ErrorCompletingRequestException;

import com.ibm.as400.access.IllegalObjectTypeException;

import com.ibm.as400.access.KeyedDataQueue;

import com.ibm.as400.access.KeyedDataQueueEntry;

import com.ibm.as400.access.ObjectDoesNotExistException;

 

/**

 * Class encapsulating methods to read a data queue from a thread so the caller

 * does not wait for the data queue read.  The results of the data queue read are

 * communicated through PropertyChangeSupport.  The varible objects are supplied

 * to the class <ul>

 * <li>AS400 object for data queue communications</li>

 * <li>The IFS path of the iSeries data queue objectg </li>

 * <li>The initial RecordFormat object that the model is expected to deliver its output </li>

 * </ul>

 * The key key value used to identify this view-controller/client to the

 * model/server as hex representation of last 3 bytes (least significant

 * bytes) of IP address giving a six character key.

 * <p>

 * @author Bill Blalock

 *

 */

public class ModelOutputDataQueue implements Runnable {

 

      private AS400 as400;

      private KeyedDataQueue dq;

      private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);

      private String dqKey;

      private boolean readDqFlag;

     

      /**

       * Construct ModelOutputDataQueue.  The parameters are contructed outside this class by the caller to

       * make the class independent and reusable.

       * @param _as400 Fully instantiated AS400 object who connection has been validated.

       * @param dqIseriesPath The IFS path of the data queue object on the iSeries from the root.

       * @param _dqKey value of key whose length matches the key

       * length defined for the data queue

       * @param _dqRecordFormat  The initial RecordFormat object to be read from the model output

       * data queue.  Severally differe RecordFormat could be used during the life of the class that

       * represent different data structures sent by the model/server.

       */

      public ModelOutputDataQueue( AS400 _as400, String dqIseriesPath, String _dqKey ) {

            dqKey = _dqKey;

            System.out.println("Key used is: " + dqKey);

            as400 = _as400;

            readDqFlag = true;

            dq = new KeyedDataQueue(as400, dqIseriesPath);

      }

 

      /**

       * Method to be executed by a Thread.

       */

      public void run() {

 

            // Record to be read from the model output data queue.

            KeyedDataQueueEntry keyedDqEntry = null;

            // Previous Record read from the model output data queue.

            KeyedDataQueueEntry oldKeyedDqEntry = null;

 

            // this loop won't end unless an Exception occurs or the JVM is ended

            while (readDqFlag) {

                 

                  try {

 

                        // Wait for the model response.

                        keyedDqEntry = dq.read(dqKey, 600, "EQ");

 

                        // the stopReadingDq() method called, setting readDqFlag false, and

                        // feeding the model's output data queue a dummy entry.  Since the

                        // readDqFlag is false the entry read from the data queue is

                        // thrown away, the property change event is not fired and break

                        // out of the loop, leaving the run() method.

                        if( !readDqFlag ) {

//                            System.out

//                                  .println("... the readDqFlag is false, breaking out of run() method ... ");

                              break;

                        }

                       

                        // Every 10 minutes the read will time out.  Loop and try again.  If something

                        // has happened to the data queue (like the System i was IPL'd)

                        // an exception will be thrown.  Not great but atleast this Thread

                        // won't wait forever for something which can't happen.

                        if( keyedDqEntry == null )

                              continue;

                       

                        // Pass the record object to the property change listener.

                        this.pcs.firePropertyChange("dqRead", oldKeyedDqEntry, keyedDqEntry);

                       

                        // save the record object just read as the old record object

                        oldKeyedDqEntry = keyedDqEntry;

     

                  } catch (ConnectionDroppedException e) {

                        reportException(e);

                        break;

                  } catch (InterruptedException e) {

                        reportException(e);

                        break;

                  } catch (UnsupportedEncodingException e) {

                        reportException(e);

                        break;

                  } catch (AS400SecurityException e) {

                        reportException(e);

                        break;

                  } catch (ObjectDoesNotExistException e) {

                        reportException(e);

                        break;

                  } catch ( ErrorCompletingRequestException e) {

                        reportException(e);

                        break;

                  } catch ( IllegalObjectTypeException e) {

                        reportException(e);

                        break;

                  } catch ( IOException e) {

                        reportException(e);

                        break;

                  }

            } // end of while(true) ObjectDoesNotExistException IllegalObjectTypeException

      }

 

 

      /**

       * Set boolean to break do loop and write a dummy record to the model's

       * output data queue.  This class will read it and break the read loop.

       *

       */

      public void stopReadingDq() {

            readDqFlag = false;

            try {

                  dq.write(dqKey, new String("          ") );

            }     catch( Exception e ) {

                        reportException(e);

            }

      }

 

      /**

       * Override the key set for this class in constuctor to identify this

       * view-controller/client to the model/server.  The length of

       * the key passed must match the length of the key defined for 

       * for data queue.

       * @param _dqKey new value of key whose length matches the key

       * length defined for the data queue

       */

      public void setDqKey(String _dqKey ) {

            dqKey = _dqKey;

      }

     

      /**

       * Get key used to identify this view-controller/client to the model/server.

       * @return Key used to identify this view-controller/client to the model/server.

       */

      public String getDqKey() {

            return dqKey;

      }

 

      /**

       * Report exceptions to user and to the data queue listener.  The

       * run() method of a thread can't throw exceptions.  When implementing

       * for real enhance this method to log the exceptions.

       * @param e

       */

      private void reportException(Exception e) {

            e.printStackTrace();

            // Pass the data queue exception to the property change listener.

            this.pcs.firePropertyChange("dqError", null, e);

      }

     

      /**

       * Add a PropertyChangeListener for bound property using the services of the

       * PropertyChangeSupport class.

       * @param listener

       */

      public void addPropertyChangeListener(PropertyChangeListener listener) {

            this.pcs.addPropertyChangeListener(listener);

      }

     

       /**

       * Remove a PropertyChangeListener using the services of the

       * PropertyChangeSupport class.

       * @param listener

       */

      public void removePropertyChangeListener(PropertyChangeListener listener) {

            this.pcs.removePropertyChangeListener(listener);

      }

           

}