prototype - implementation in progress JAL-591
authorjprocter <Jim Procter>
Tue, 22 Jun 2010 12:37:22 +0000 (12:37 +0000)
committerjprocter <Jim Procter>
Tue, 22 Jun 2010 12:37:22 +0000 (12:37 +0000)
src/jalview/gui/WsJobParameters.java [new file with mode: 0644]
src/jalview/ws/jws2/Jws2Discoverer.java

diff --git a/src/jalview/gui/WsJobParameters.java b/src/jalview/gui/WsJobParameters.java
new file mode 100644 (file)
index 0000000..7c4391e
--- /dev/null
@@ -0,0 +1,887 @@
+package jalview.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.ContainerEvent;
+import java.awt.event.ContainerListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
+import java.awt.event.WindowStateListener;
+import java.util.ArrayList;
+import java.util.EventObject;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
+import javax.swing.border.TitledBorder;
+import javax.swing.event.CellEditorListener;
+import javax.swing.table.*;
+
+import compbio.metadata.Argument;
+import compbio.metadata.Option;
+import compbio.metadata.Parameter;
+import compbio.metadata.Preset;
+import compbio.metadata.PresetManager;
+import compbio.metadata.RunnerConfig;
+import compbio.metadata.ValueConstrain;
+import compbio.metadata.ValueConstrain.Type;
+
+import jalview.ws.jws2.Jws2Discoverer;
+import jalview.ws.jws2.Jws2Discoverer.Jws2Instance;
+
+public class WsJobParameters extends JPanel
+{
+  JPanel setDetails = new JPanel();
+
+  JPanel settingsPanel = new JPanel();
+
+  JPanel jobOptions = new JPanel();
+
+  JPanel jobParameters = new JPanel();
+
+  JPanel paramButtons = new JPanel();
+
+  JButton exportParams = new JButton();
+
+  JButton importParams = new JButton();
+
+  JButton resetParams = new JButton();
+
+  JButton addParam = new JButton();
+
+  JButton removeParam = new JButton();
+
+  JButton editParam = new JButton();
+
+  JLabel setName = new JLabel();
+
+  JTextArea setDescr = new JTextArea();
+
+  JTable paramTable = new JTable();
+
+  JScrollPane paramPane = new JScrollPane();
+
+  RunnerConfig serviceOptions;
+
+  List<Parameter> availableParameters;
+
+  private BorderLayout jparamLayout;
+
+  WsJobParameters(Jws2Instance service)
+  {
+    this(service, null);
+  }
+
+  public WsJobParameters(Jws2Instance service, Preset p)
+  {
+    this(null, service, p);
+  }
+
+  /**
+   * 
+   * @param desktop
+   *          - if null, create new JFrame outside of desktop
+   * @param service
+   * @param p
+   */
+  public WsJobParameters(JFrame parent, Jws2Instance service, Preset p)
+  {
+    super();
+    jbInit();
+    initTableUI();
+    // populate parameter table
+    initForService(service, p);
+    // display in new JFrame attached to parent.
+    validate();
+  }
+
+  private void jbInit()
+  {
+    exportParams.setFont(new java.awt.Font("Verdana", Font.PLAIN, 10));
+    exportParams.setText("Export");
+    exportParams.setToolTipText("Export parameters to a file");
+    exportParams.addActionListener(new ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        exportParams_actionPerformed(e);
+      }
+    });
+    importParams.setFont(new java.awt.Font("Verdana", Font.PLAIN, 10));
+    importParams.setText("Import");
+    importParams.setToolTipText("Import parameters from a file");
+    importParams.addActionListener(new ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        importParams_actionPerformed(e);
+      }
+    });
+    resetParams.setFont(new java.awt.Font("Verdana", Font.PLAIN, 10));
+    resetParams.setText("Defaults");
+    resetParams.setToolTipText("Reset to defaults");
+    resetParams.addActionListener(new ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        resetParams_actionPerformed(e);
+      }
+    });
+    addParam.setFont(new java.awt.Font("Verdana", Font.PLAIN, 10));
+    addParam.setText("Add");
+    addParam.setToolTipText("Add new parameter");
+    addParam.addActionListener(new ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        addParam_actionPerformed(e);
+      }
+    });
+    removeParam.setFont(new java.awt.Font("Verdana", Font.PLAIN, 10));
+    removeParam.setText("Remove");
+    removeParam.setToolTipText("Remove selected parameter");
+    removeParam.addActionListener(new ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        removeParam_actionPerformed(e);
+      }
+    });
+    editParam.setFont(new java.awt.Font("Verdana", Font.PLAIN, 10));
+    editParam.setText("Edit");
+    editParam.setToolTipText("Edit selected parameter");
+    editParam.addActionListener(new ActionListener()
+    {
+      public void actionPerformed(ActionEvent e)
+      {
+        editParam_actionPerformed(e);
+      }
+    });
+
+    setDetails.setBorder(new TitledBorder("Details"));
+    setDetails.setLayout(new GridLayout(2, 1));
+    setDescr.setColumns(40);
+    setDescr.setWrapStyleWord(true);
+    setDescr.setPreferredSize(new Dimension(350, 100));
+    setDetails.add(setName);
+    setDetails.add(setDescr);
+    setDetails.setPreferredSize(new Dimension(360, 150));
+    settingsPanel.setLayout(new BorderLayout());
+    jobOptions.setBorder(new TitledBorder("Options"));
+    jobParameters.setBorder(new TitledBorder("Parameters"));
+    jobParameters.setLayout(jparamLayout = new BorderLayout());
+    paramPane.setPreferredSize(new Dimension(360, 300));
+    paramButtons.setLayout(new GridLayout(2, 3));
+    paramButtons.add(addParam);
+    paramButtons.add(editParam);
+    paramButtons.add(removeParam);
+    // TODO: relayout buttons nicely
+    paramButtons.add(exportParams);
+    paramButtons.add(importParams);
+    paramButtons.add(resetParams);
+    jobParameters.add(paramPane, BorderLayout.NORTH);
+    jobParameters.add(paramButtons, BorderLayout.SOUTH);
+    settingsPanel.add(jobOptions, BorderLayout.NORTH);
+    settingsPanel.add(jobParameters, BorderLayout.SOUTH);
+    setLayout(new BorderLayout());
+    setPreferredSize(new Dimension(400, 600));
+    setSize(new Dimension(400, 600));
+    add(setDetails, BorderLayout.NORTH);
+    add(settingsPanel, BorderLayout.CENTER);
+  }
+
+  /**
+   * need to populate table and validate after calling this to set up table and
+   * add it to pane
+   */
+  public void initTableUI()
+  {
+    paramTable = new JTable();
+    paramTable.getTableHeader()
+            .setFont(new Font("Verdana", Font.PLAIN, 12));
+    paramTable.setFont(new Font("Verdana", Font.PLAIN, 12));
+
+    paramPane.getViewport().removeAll();
+    paramPane.getViewport().add(paramTable);
+    paramTable.setDefaultRenderer(Argument.class, new ArgumentRenderer());
+    paramTable.setDefaultRenderer(Parameter.class, new ArgumentRenderer());
+    paramTable.setDefaultRenderer(Option.class, new ArgumentRenderer());
+    paramTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+    paramTable.addMouseListener(new MouseListener()
+    {
+
+      @Override
+      public void mouseClicked(MouseEvent e)
+      {
+        if (e.getClickCount() > 1)
+        {
+          editParam_actionPerformed(null);
+        }
+      }
+
+      @Override
+      public void mouseEntered(MouseEvent e)
+      {
+        // tooltips
+        // TODO Auto-generated method stub
+
+      }
+
+      @Override
+      public void mouseExited(MouseEvent e)
+      {
+        // tooltips
+        // TODO Auto-generated method stub
+
+      }
+
+      @Override
+      public void mousePressed(MouseEvent e)
+      {
+        // TODO Auto-generated method stub
+
+      }
+
+      @Override
+      public void mouseReleased(MouseEvent e)
+      {
+        // TODO Auto-generated method stub
+
+      }
+
+    });
+    // paramTable.setDefaultEditor(Argument.class, new WsArgumentEditor(this));
+  }
+
+  public class WsArgumentEditor implements TableCellEditor
+  {
+
+    int p;
+
+    Argument arg;
+
+    WsJobParameters wsparm;
+
+    public WsArgumentEditor(WsJobParameters wsJobParameters)
+    {
+      wsparm = wsJobParameters;
+      p = wsparm.paramTable.getSelectedRow();
+      arg = (Argument) wsparm.paramTable.getValueAt(p, wsparm.paramTable
+              .getSelectedColumn());
+      /*
+       * if (arg instanceof Option) { if
+       * (JOptionPane.showInternalConfirmDialog(Desktop.desktop,
+       * "Remove option "+arg.getName(),
+       * "Delete option ?",JOptionPane.YES_NO_OPTION,
+       * JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) {
+       * 
+       * } }
+       */
+    }
+
+    @Override
+    public Component getTableCellEditorComponent(JTable table,
+            Object value, boolean isSelected, int row, int column)
+    {
+      // TODO Auto-generated method stub
+      return null;
+    }
+
+    @Override
+    public void addCellEditorListener(CellEditorListener l)
+    {
+      // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void cancelCellEditing()
+    {
+      // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public Object getCellEditorValue()
+    {
+      // TODO Auto-generated method stub
+      return null;
+    }
+
+    @Override
+    public boolean isCellEditable(EventObject anEvent)
+    {
+      // TODO Auto-generated method stub
+      return false;
+    }
+
+    @Override
+    public void removeCellEditorListener(CellEditorListener l)
+    {
+      // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public boolean shouldSelectCell(EventObject anEvent)
+    {
+      // TODO Auto-generated method stub
+      return false;
+    }
+
+    @Override
+    public boolean stopCellEditing()
+    {
+      // TODO Auto-generated method stub
+      return false;
+    }
+
+  }
+
+  protected void editParam_actionPerformed(ActionEvent e)
+  {
+    // TODO Auto-generated method stub
+
+  }
+
+  protected void removeParam_actionPerformed(ActionEvent e)
+  {
+    // TODO Auto-generated method stub
+
+  }
+
+  protected void addParam_actionPerformed(ActionEvent e)
+  {
+    // TODO Auto-generated method stub
+
+  }
+
+  protected void resetParams_actionPerformed(ActionEvent e)
+  {
+    // TODO Auto-generated method stub
+
+  }
+
+  protected void importParams_actionPerformed(ActionEvent e)
+  {
+    // TODO Auto-generated method stub
+
+  }
+
+  protected void exportParams_actionPerformed(ActionEvent e)
+  {
+    // TODO Auto-generated method stub
+
+  }
+
+  void initForService(Jws2Instance service, Preset p)
+  {
+    serviceOptions = service.getRunnerConfig();
+    updateTable(p);
+  }
+
+  @SuppressWarnings("unchecked")
+  private void updateTable(Preset p)
+  {
+    WsParameterTableModel mdl = null;
+    TableModel tmdl = paramTable.getModel();
+    if (tmdl instanceof WsParameterTableModel)
+    {
+      mdl = (WsParameterTableModel) tmdl;
+    }
+    if (mdl == null)
+    {
+      paramTable.setModel(mdl = new WsParameterTableModel());
+    }
+    availableParameters = new ArrayList<Parameter>();
+    List<Parameter> setargs = new ArrayList<Parameter>();
+    // populate table from default parameter set.
+    List<Argument> args = serviceOptions.getArguments();
+    
+    // split to params and required arguments
+    {
+      for (Argument arg : args)
+      {
+        Argument myarg = (Argument) arg;
+        // Ideally, Argument would implement isRequired !
+        if (myarg instanceof Parameter)
+        {
+          Parameter parm = (Parameter) myarg;
+          if (parm.isRequired())
+          {
+            setargs.add(parm);
+          }
+          else
+          {
+            availableParameters.add(parm);
+          }
+        }
+        else
+        {
+          if (myarg instanceof Option)
+          {
+            Option opt = (Option) myarg;
+            addOption(opt);
+            if (opt.isRequired())
+            {
+              selectOption(opt, opt.getDefaultValue());
+            }
+          }
+          else
+          {
+            System.err.println("Ignoring unknown service argument type "
+                    + arg.getClass().getName());
+          }
+        }
+      }
+    }
+
+    if (p != null)
+    {
+      setDescr.setText(p.getDescription());
+      setName.setText(p.getName());
+      // TODO - URL link
+      try
+      {
+        args = p.getArguments(serviceOptions);
+      } catch (Exception e)
+      {
+        e.printStackTrace();
+      }
+      setargs.clear();
+      // get setargs from current object
+      if (args != null)
+      {
+        for (Argument arg : args)
+        {
+          if (arg instanceof Parameter)
+          {
+            setargs.add((Parameter) arg);
+          }
+          else
+          {
+            if (arg instanceof Option)
+            {
+              selectOption((Option) arg, arg.getDefaultValue());
+            }
+          }
+
+        }
+      }
+    }
+
+    mdl.setData(setargs);
+    paramTable.setModel(mdl);
+    // paramTable.validate();
+    jobOptions.setLayout(new GridLayout(optSet.size() / 2 + 1, 2));
+    jobOptions.validate();
+  }
+
+  private void selectOption(Option opt, String string)
+  {
+    OptionBox cb = optSet.get(opt.getName());
+    if (cb == null)
+    {
+      cb = addOption(opt);
+    }
+    if (string == null)
+    {
+      // no value specified.
+      if (opt.isRequired())
+      {
+        // indicate option needs to be selected!
+      }
+      else
+      {
+        cb.enabled.setSelected(false);
+      }
+    }
+    else
+    {
+      cb.enabled.setSelected(true);
+
+      if (opt.getPossibleValues().contains(string))
+      {
+        cb.val.setSelectedItem(string);
+      }
+      else
+      {
+        throw new Error("Invalid value " + string + " for option " + opt);
+      }
+
+    }
+  }
+
+  Map<String, OptionBox> optSet = new Hashtable<String, OptionBox>();
+
+  public class OptionBox extends JPanel
+  {
+    JComboBox val = new JComboBox();
+
+    JCheckBox enabled = new JCheckBox();
+
+    public OptionBox(Option opt)
+    {
+      setLayout(new BorderLayout());
+      enabled.setSelected(opt.isRequired());
+      enabled.setFont(new Font("Verdana", Font.PLAIN, 11));
+      enabled.setText(opt.getName());
+      enabled.setToolTipText(opt.getDescription());
+      add(enabled, BorderLayout.NORTH);
+      if (opt.getPossibleValues().size() > 1)
+      {
+        setLayout(new GridLayout(1,2));
+        for (Object str : opt.getPossibleValues())
+        {
+          val.addItem((String) str);
+        }
+        val.setSelectedItem((String) opt.getDefaultValue());
+        add(val, BorderLayout.SOUTH);
+      }
+      // TODO: add actionListeners for popup (to open further info), 
+      // and to update list of parameters if an option is enabled
+      // that takes a value.
+    }
+  }
+
+  private OptionBox addOption(Option opt)
+  {
+    OptionBox cb = optSet.get(opt.getName());
+    if (cb == null)
+    {
+      cb = new OptionBox(opt);
+      optSet.put(opt.getName(), cb);
+      jobOptions.add(cb);
+    }
+    return cb;
+  }
+
+  class WsParameterTableModel extends AbstractTableModel
+  {
+    // arg list - vector for thread safety
+    public Vector<Parameter> data = new Vector<Parameter>();
+
+    private String[] colNames =
+    { "Parameter", "Value" };
+
+    @Override
+    public int getColumnCount()
+    {
+      return 2;
+    }
+
+    @Override
+    public int findColumn(String columnName)
+    {
+      // TODO Auto-generated method stub
+      return super.findColumn(columnName);
+    }
+
+    public void setData(List<Parameter> setargs)
+    {
+      if (setargs != null)
+      {
+        data = new Vector<Parameter>(setargs);
+
+      }
+      else
+      {
+        data = new Vector<Parameter>();
+      }
+    }
+
+    public List<Parameter> getData()
+    {
+      return new ArrayList<Parameter>(data);
+    }
+
+    @Override
+    public int getRowCount()
+    {
+      return data.size();
+    }
+
+    @Override
+    public Object getValueAt(int rowIndex, int columnIndex)
+    {
+      if (rowIndex >= 0 && rowIndex < data.size())
+      {
+        Parameter prm = data.elementAt(rowIndex);
+        switch (columnIndex)
+        {
+        case 0:
+          return prm.getName();
+        case 1:
+          Object exc = null;
+          // the accessor name for the parameter value is possibly confusing
+          String val = prm.getDefaultValue();
+          try
+          {
+            if (prm.getValidValue().getType().equals(Type.Float))
+            {
+              exc = new Float(0);
+              return Float.parseFloat(val);
+            }
+            if (prm.getValidValue().getType().equals(Type.Integer))
+            {
+              exc = new Integer(0);
+              return Integer.parseInt(val);
+            }
+          } catch (NumberFormatException e)
+          {
+            return exc;
+          }
+          // must be a string
+          return val;
+
+        default:
+        }
+      }
+      return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see javax.swing.table.AbstractTableModel#getColumnClass(int)
+     */
+    @Override
+    public Class<?> getColumnClass(int columnIndex)
+    {
+      switch (columnIndex)
+      {
+      case 0:
+        return String.class;
+      case 1:
+        return Parameter.class;
+      }
+      return super.getColumnClass(columnIndex);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see javax.swing.table.AbstractTableModel#getColumnName(int)
+     */
+    @Override
+    public String getColumnName(int column)
+    {
+      return colNames[column];
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see javax.swing.table.AbstractTableModel#isCellEditable(int, int)
+     */
+    @Override
+    public boolean isCellEditable(int rowIndex, int columnIndex)
+    {
+      if (columnIndex == 1)
+      {
+        return true;
+      }
+      return false;
+    }
+  }
+
+  class ArgumentRenderer implements TableCellRenderer
+  {
+
+    @Override
+    public Component getTableCellRendererComponent(JTable table,
+            Object value, boolean isSelected, boolean hasFocus, int row,
+            int column)
+    {
+      if (value instanceof Option)
+      {
+        JCheckBox cb = new JCheckBox();
+        cb.setSelected(true);
+        return cb;
+      }
+      if (value instanceof Boolean)
+      {
+        JCheckBox cb = new JCheckBox();
+        cb.setSelected(((Boolean) value).booleanValue());
+        return cb;
+      }
+      if (value instanceof String)
+      {
+        JLabel lbl = new JLabel();
+        lbl.setFont(new Font("Verdana", Font.PLAIN, 11));
+        lbl.setText((String) value);
+        return lbl;
+      }
+      if (value instanceof Parameter)
+      {
+        String val = ((Parameter) value).getDefaultValue();
+        ValueConstrain constr = ((Parameter) value).getValidValue();
+        JLabel lbl = new JLabel();
+        lbl.setFont(new Font("Verdana", Font.PLAIN, 11));
+        lbl.setText(val);
+        return lbl;
+        /*
+         * type dependent return value if
+         * (constr.getType().equals(Type.Integer))
+         * 
+         * { lbl.setText(val); } if (constr.getType().equals(Type.Float)) { }
+         * return lbl;
+         */
+      }
+      return null;
+    }
+  }
+
+  /**
+   * testing method - grab a service and parameter set and show the window
+   * 
+   * @param args
+   */
+  public static void main(String[] args)
+  {
+    jalview.ws.jws2.Jws2Discoverer disc = jalview.ws.jws2.Jws2Discoverer
+            .getDiscoverer();
+    int p = 0;
+    if (args.length > 3)
+    {
+      Vector<String> services = new Vector<String>();
+      services.addElement(args[p++]);
+      Jws2Discoverer.setServiceUrls(services);
+    }
+    try
+    {
+      disc.run();
+    } catch (Exception e)
+    {
+      System.err.println("Aborting. Problem discovering services.");
+      e.printStackTrace();
+      return;
+    }
+    Jws2Discoverer.Jws2Instance lastserv = null;
+    for (Jws2Discoverer.Jws2Instance service : disc.getServices())
+    {
+      lastserv = service;
+      if (p < args.length && service.serviceType.equalsIgnoreCase(args[p]))
+      {
+        break;
+      }
+    }
+    if (lastserv != null)
+    {
+      Preset pr = null;
+      if (++p < args.length)
+      {
+        PresetManager prman = lastserv.getPresets();
+        if (prman != null)
+        {
+          pr = prman.getPresetByName(args[p]);
+          if (pr == null)
+          {
+            // just grab the last preset.
+            List<Preset> prl = prman.getPresets();
+            pr = prl.get(prl.size() - 1);
+          }
+        }
+      }
+      while (true)
+      {
+        WsJobParameters pgui = new WsJobParameters(lastserv, pr);
+        JFrame jf = new JFrame("Parameters for " + lastserv.getActionText());
+        JPanel cont = new JPanel();
+        cont.setSize(new Dimension(600, 800));
+        cont.add(pgui);
+        jf.add(cont);
+        final Thread thr = Thread.currentThread();
+        jf.addWindowListener(new WindowListener()
+        {
+
+          @Override
+          public void windowActivated(WindowEvent e)
+          {
+            // TODO Auto-generated method stub
+
+          }
+
+          @Override
+          public void windowClosed(WindowEvent e)
+          {
+          }
+
+          @Override
+          public void windowClosing(WindowEvent e)
+          {
+            thr.interrupt();
+
+          }
+
+          @Override
+          public void windowDeactivated(WindowEvent e)
+          {
+            // TODO Auto-generated method stub
+
+          }
+
+          @Override
+          public void windowDeiconified(WindowEvent e)
+          {
+            // TODO Auto-generated method stub
+
+          }
+
+          @Override
+          public void windowIconified(WindowEvent e)
+          {
+            // TODO Auto-generated method stub
+
+          }
+
+          @Override
+          public void windowOpened(WindowEvent e)
+          {
+            // TODO Auto-generated method stub
+
+          }
+
+        });
+        jf.setVisible(true);
+        boolean inter = false;
+        while (!inter)
+        {
+          try
+          {
+            Thread.sleep(10000);
+          } catch (Exception e)
+          {
+            inter = true;
+          }
+          ;
+        }
+        jf.dispose();
+      }
+    }
+  }
+}
index d982981..918abce 100644 (file)
@@ -21,8 +21,10 @@ import jalview.datamodel.AlignmentView;
 import jalview.gui.AlignFrame;
 import jalview.ws.WSMenuEntryProviderI;
 import compbio.data.msa.MsaWS;
+import compbio.metadata.Option;
 import compbio.metadata.Preset;
 import compbio.metadata.PresetManager;
+import compbio.metadata.RunnerConfig;
 import compbio.ws.client.Jws2Base;
 import compbio.ws.client.Jws2Base.Services;
 
@@ -160,11 +162,11 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
 
   public class Jws2Instance
   {
-    String hosturl;
+    public String hosturl;
 
-    String serviceType;
+    public String serviceType;
 
-    MsaWS service;
+    public MsaWS service;
 
     public Jws2Instance(String hosturl, String serviceType, MsaWS service)
     {
@@ -224,12 +226,21 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
       return "Align with "
       + serviceType;
     }
+
+    /**
+     * non-thread safe - blocks whilst accessing service to get complete set of available options and parameters
+     * @return
+     */
+    public RunnerConfig getRunnerConfig()
+    {
+      return service.getRunnerOptions();
+    }
   };
 
   /**
    * holds list of services.
    */
-  Vector<Jws2Instance> services;
+  protected Vector<Jws2Instance> services;
 
   /**
    * find or add a submenu with the given title in the given menu
@@ -432,4 +443,9 @@ public class Jws2Discoverer implements Runnable, WSMenuEntryProviderI
     return null;
   }
 
+  public Vector<Jws2Instance> getServices()
+  {
+    return (services==null) ? new Vector<Jws2Instance>(): new Vector<Jws2Instance>(services);
+  }
+
 }