[MOBY-guts] biomoby commit
Eddie Kawas
kawas at dev.open-bio.org
Tue May 27 17:25:17 UTC 2008
kawas
Tue May 27 13:25:17 EDT 2008
Update of /home/repository/moby/moby-live/Java/src/main/org/biomoby/service/dashboard
In directory dev.open-bio.org:/tmp/cvs-serv11158/src/main/org/biomoby/service/dashboard
Modified Files:
PerlMoSeSPanel.java
Log Message:
*the detection for MOSES::MOBY is now silent.
*added a subpanel to the perl-moses panel that allows users to use a locally installed version of moses-moby.
moby-live/Java/src/main/org/biomoby/service/dashboard PerlMoSeSPanel.java,1.3,1.4
===================================================================
RCS file: /home/repository/moby/moby-live/Java/src/main/org/biomoby/service/dashboard/PerlMoSeSPanel.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- /home/repository/moby/moby-live/Java/src/main/org/biomoby/service/dashboard/PerlMoSeSPanel.java 2008/05/20 15:33:00 1.3
+++ /home/repository/moby/moby-live/Java/src/main/org/biomoby/service/dashboard/PerlMoSeSPanel.java 2008/05/27 17:25:17 1.4
@@ -59,46 +59,77 @@
import org.apache.commons.io.FilenameUtils;
import org.biomoby.shared.MobyException;
+import org.tulsoft.tools.gui.JTextFieldWithHistory;
import org.tulsoft.tools.gui.SwingUtils;
public class PerlMoSeSPanel extends AbstractPanel {
// some properties for the channel
+ // String: set this to pop up a warning message
static String PM_WARNING = "perl-moses-warning";
+ // String: set this to pop up an error message
static String PM_ERROR = "perl-moses-error";
+ // String: set this for an information message
static String PM_INFORMATION = "perl-moses-information";
+ // boolean - do we over write files
static String PM_OVERWRITE = "perl-moses-overwrite";
+ // boolean: do we generate cgi services
static String PM_CGI = "perl-moses-cgi";
+ // boolean do we generate soap services
static String PM_SOAP = "perl-moses-soap";
+ // boolean: do we update the service cache
static String PM_UPDATE_SERVICE_CACHE = "perl-moses-update-service-cache";
+ // boolean: do we update the datatype cache
static String PM_UPDATE_DATATYPE_CACHE = "perl-moses-update-datatype-cache";
+ // boolean: are we currently updating the datatype cache
static String PM_SYNC_DATATYPES = "perl-moses-sync-datatypes";
+ // boolean: are we currently updating the services cache
static String PM_SYNC_SERVICES = "perl-moses-sync-services";
+ // boolean: are we generating services by name
static String PM_GENERATING_SPECIFIC_SERVICES = "perl-moses-generating-specific-services";
+ // boolean: are we generating services by authority
static String PM_GENERATING_BY_AUTHORITY = "perl-moses-generating-by-authority";
+ // N/A: set this to make the editor save the current file
static String PM_FILE_ACTION_SAVE = "perl-moses-file-action-save";
+ // N/A: set this to make the editor open a file
static String PM_FILE_ACTION_OPEN = "perl-moses-file-action-open";
+ // N/A: set this to make the editor close a file
static String PM_FILE_ACTION_CLOSE = "perl-moses-file-action-close";
+ // String: the full path of the file that we are editing
static String PM_FILE_CURRENT = "perl-moses-file-current";
+ // String: the checksum of the current open file
static String PM_FILE_CURRENT_CHECKSUM = "perl-moses-file-current-checksum";
+ // String: the parent directory of the last opened file
static String PM_FILE_LAST_DIRECTORY = "perl-moses-file-last-directory";
+
+ // String: one of the PERL5LIB directories for those that install modules locally
+ static String PM_PERL_LIB_1 = "perl-moses-perl-lib-1";
+
+ // String: one of the PERL5LIB directories for those that install modules locally
+ static String PM_PERL_LIB_2 = "perl-moses-perl-lib-2";
+
+ // String: the exact path to the locally installed moses scripts
+ static String PM_SCRIPTS_INSTALL_DIR = "perl-moses-scripts-install-dir";
+
+ // String: the exact path to the users perl (in case it is not available on the path)
+ static String PM_PERL_INSTALL_DIR = "perl-moses-perl-install-dir";
// generated serialversionUID
private static final long serialVersionUID = 1379466985182986776L;
@@ -109,52 +140,95 @@
private JLabel aSelectedCount, sSelectedCount, currentlyEditing;
- private JButton generateBtn;
-
- private JButton syncBtn;
-
- private JButton editorSaveButton, editorCloseButton, editorOpenButton;
+ private JButton editorSaveButton, editorCloseButton, editorOpenButton, scriptsBtn, generateBtn, syncBtn;
+ // are we enabling moses actions
private boolean enable_moses_actions = true;
+ // is the user running windows
private boolean is_windows_pc = false;
- private Icon openFileIcon, openFileIconDis;
-
- private Icon closeFileIcon, closeFileIconDis;
-
- private Icon saveFileIcon, saveFileIconDis;
-
- private Icon zoomInIcon, zoomInIconDis;
- private Icon zoomOutIcon, zoomOutIconDis;
+ private Icon openFileIcon, openFileIconDis, closeFileIcon,
+ closeFileIconDis, saveFileIcon, saveFileIconDis,
+ zoomInIcon, zoomInIconDis, zoomOutIcon, zoomOutIconDis;
private static JTextPane editorTextPane;
private JComboBox fonts;
+ /**
+ * Default constructor: set the image, and determines
+ * whether or not we are using windows
+ */
public PerlMoSeSPanel() {
super();
panelIconFileName = "images/pMoses.gif";
+
if (System.getProperty("os.name").startsWith("Windows"))
is_windows_pc = true;
// determine if perl & moses are installed
- String[] command;
+ }
+
+ /*
+ * private method that determines if
+ * moses is available from the command line.
+ */
+ private void check_install() {
+ String[] command;
+ ArrayList<String> prefix = generatePerlPrefix();
+
if (is_windows_pc) {
- command = new String[] {"moses-cache-tester.bat"};
+ prefix.add("moses-cache-tester.bat");
+ command = prefix.toArray(new String[]{});
} else {
- command = new String[] {"moses-cache-tester.pl"};
+ prefix.add((prefix.size() > 0 ? prefix.remove(prefix.size()-1) : "") + "moses-cache-tester.pl");
+ command = prefix.toArray(new String[]{});
}
-
try {
Process process;
process = Runtime.getRuntime().exec(command);
- process.waitFor();
+ process.waitFor();
+ enable_moses_actions = true;
} catch (Exception e) {
enable_moses_actions = false;
}
}
/*
+ * private method that generates the
+ *
+ * /path/to/perl -I/some/dir /path/to/scripts
+ *
+ * part of the perl call
+ *
+ */
+ private ArrayList<String> generatePerlPrefix() {
+ // usually windows installs everything as root
+ if (is_windows_pc)
+ return new ArrayList<String>();
+
+ ArrayList<String> list = new ArrayList<String>();
+ String script_path = propertyChannel.getString(PM_SCRIPTS_INSTALL_DIR);
+ String lib1 = propertyChannel.getString(PM_PERL_LIB_1);
+ String lib2 = propertyChannel.getString(PM_PERL_LIB_2);
+ String perl = propertyChannel.getString(PM_PERL_INSTALL_DIR);
+
+ if (perl != null && !perl.trim().equals("") && script_path != null && !script_path.trim().equals("")) {
+ list.add(perl);
+ if (lib1 != null && !lib1.trim().equals("")) {
+ list.add("-I" + lib1.trim());
+ }
+ if (lib2 != null && !lib2.trim().equals("")) {
+ list.add("-I" + lib2.trim());
+ }
+ if (!script_path.endsWith("/"))
+ script_path += "/";
+ list.add(script_path);
+ }
+ return list;
+ }
+
+ /*
* (non-Javadoc)
*
* @see org.biomoby.service.dashboard.AbstractPanel#getComponent(org.biomoby.service.dashboard.PropertyChannel)
@@ -162,18 +236,8 @@
@Override
public JComponent getComponent(PropertyChannel propertyChannel) {
- if (!enable_moses_actions) {
- JOptionPane.showMessageDialog(
- null,
- "Sorry, Perl-MoSeS is not enabled because we couldn't detect" +
- "\nit on your system. Please make sure that it is installed\n" +
- "on your machine!", "Perl-MoSeS",JOptionPane.ERROR_MESSAGE);
- JPanel p = new JPanel(new BorderLayout(), true);
- JLabel l = new JLabel("Please install Perl-MoSes from CPAN before continuing!");
- p.add(l, BorderLayout.PAGE_START);
- return p;
- }
- setPropertyChannel(propertyChannel);
+ setPropertyChannel(propertyChannel);
+
determineMoSeSDirectory();
// put some properties in the channel
propertyChannel.put(PM_GENERATING_BY_AUTHORITY, new Boolean(false));
@@ -182,7 +246,7 @@
propertyChannel.put(PM_SYNC_DATATYPES, new Boolean(false));
propertyChannel.put(PM_OVERWRITE, new Boolean(false));
propertyChannel.put(PM_CGI, new Boolean(false));
-
+
propertyChannel.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
if (e.getPropertyName().equals(PM_ERROR)) {
@@ -241,20 +305,21 @@
// set up the splitpane
JSplitPane vsplit = vSplit(getServicesSelectionPanel(), tabbedPane, 0.8);
JSplitPane splitPane = hSplit(vsplit, getEditorPanel(), 0.1);
- splitPane.resetToPreferredSizes();
// add the components
SwingUtils.addComponent(pComponent, splitPane, 0, 0, 1, 1, BOTH, NWEST, 1.0, 1.0);
return pComponent;
}
+ /*
+ * method for setting what we think is the Perl-MoSeS home
+ * directory
+ */
private void determineMoSeSDirectory() {
try {
// set PM_FILE_LAST_DIRECTORY
File f = new File(System.getProperty("user.home"),"Perl-MoSeS");
if (f.exists() && f.isDirectory() && f.canRead() && f.canWrite())
propertyChannel.put(PM_FILE_LAST_DIRECTORY, f.getPath());
-
-
} catch (Exception e) {
}
@@ -262,11 +327,70 @@
}
private JPanel getMoSeSPanel() {
+
+ check_install();
JPanel p = new JPanel (new GridBagLayout());
p.setBorder (createFatBorder
("Perl-MoSeS",
GraphColours.getColour ("cadetblue", Color.blue)));
+ JPanel sPanel = createTitledPanel("Local User Config");
+ if (!enable_moses_actions) {
+ scriptsBtn = createButton(
+ "Test user config",
+ "Check whether or not your local user config is correct",
+ KeyEvent.VK_R, new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (!enable_moses_actions) {
+ if (propertyChannel.getString(PM_SCRIPTS_INSTALL_DIR) != null
+ && !propertyChannel.getString(PM_SCRIPTS_INSTALL_DIR).endsWith("/"))
+ propertyChannel.put(
+ PM_SCRIPTS_INSTALL_DIR,
+ propertyChannel.getString(PM_SCRIPTS_INSTALL_DIR) + "/");
+ // check for the scripts
+ check_install();
+ if (enable_moses_actions) {
+ // we are good to go! enable buttons
+ syncBtn.setEnabled(enable_moses_actions);
+ generateBtn.setEnabled(enable_moses_actions);
+ scriptsBtn.setEnabled(!enable_moses_actions);
+ propertyChannel.fire(PM_INFORMATION, "Your local configuration was successful!");
+
+ } else {
+ // alert them that this directory didnt
+ // contain anything useful
+
+ propertyChannel.fire(PM_ERROR,
+ "Sorry, your configuration resulted in error.\n" +
+ "Please review your values and try again!");
+ }
+ }
+ }
+ });
+
+ JTextFieldWithHistory lib1 = createText (null, PM_PERL_LIB_1, PM_PERL_LIB_1);
+ JTextFieldWithHistory lib2 = createText (null, PM_PERL_LIB_2, PM_PERL_LIB_2);
+ JTextFieldWithHistory install_path = createText (null, PM_SCRIPTS_INSTALL_DIR, PM_SCRIPTS_INSTALL_DIR);
+ JTextFieldWithHistory perl_install_path = createText(null, PM_PERL_INSTALL_DIR, PM_PERL_INSTALL_DIR);
+
+ SwingUtils.addComponent(sPanel, new JLabel("Perl Path: "), 0, 0, 1, 1, NONE, NWEST, 0.0, 0.0);
+ SwingUtils.addComponent(sPanel, perl_install_path, 1, 0, 3, 1, HORI, NWEST, 1.0, 0.0);
+ SwingUtils.addComponent(sPanel, new JLabel("PERL5LIB dir: "), 0, 1, 1, 1, NONE, NWEST, 0.0, 0.0);
+ SwingUtils.addComponent(sPanel, lib1, 1, 1, 3, 1, HORI, NWEST, 1.0, 0.0);
+ SwingUtils.addComponent(sPanel, new JLabel("PERL5LIB dir (2): "), 0, 2, 1, 1, NONE, NWEST, 0.0, 0.0);
+ SwingUtils.addComponent(sPanel, lib2, 1, 2, 3, 1, HORI, NWEST, 1.0, 0.0);
+ SwingUtils.addComponent(sPanel, new JLabel("MoSeS Scripts dir: "),0, 3, 1, 1, NONE, NWEST, 0.0, 0.0);
+ SwingUtils.addComponent(sPanel, install_path, 1, 3, 3, 1, HORI, NWEST, 1.0, 0.0);
+ SwingUtils.addComponent(sPanel, scriptsBtn, 1, 4, 3, 1, REMAINDER, NWEST, 1.0, 0.0);
+ } else {
+ // we dont show the config sub panel
+ // remove the PERL5LIB, ScriptsInstallDir and PerlInstallDir so that they dont cause problems
+ propertyChannel.put(PM_PERL_INSTALL_DIR, "");
+ propertyChannel.put(PM_PERL_LIB_1, "");
+ propertyChannel.put(PM_PERL_LIB_2, "");
+ propertyChannel.put(PM_SCRIPTS_INSTALL_DIR, "");
+ }
+
JPanel bPanel = createTitledPanel("Generate");
JCheckBox genOverwrite = createActionBox("Overwrite Existing Code",
PM_OVERWRITE);
@@ -290,7 +414,7 @@
"Processing MoSeS skeletons...");
if (propertyChannel
.get(DashboardProperties.DP_SEL_SERVICES) != null) {
- propertyChannel.fire(
+ propertyChannel.put(
PM_GENERATING_SPECIFIC_SERVICES,
new Boolean(true));
onGenerateSpecificService();
@@ -299,7 +423,7 @@
if (propertyChannel
.get(DashboardProperties.DP_SEL_AUTHORITIES) != null) {
- propertyChannel.fire(PM_GENERATING_BY_AUTHORITY,
+ propertyChannel.put(PM_GENERATING_BY_AUTHORITY,
new Boolean(true));
onGenerateFromAuthority();
}
@@ -307,7 +431,7 @@
"Processing MoSeS skeletons completed");
}
});
-
+ generateBtn.setEnabled(enable_moses_actions);
ButtonGroup group = new ButtonGroup();
JRadioButton genCgi, genSoap;
group.add (genCgi = createHowToButton ("Generate CGI Service", PM_CGI));
@@ -315,8 +439,8 @@
SwingUtils.addComponent(bPanel, genOverwrite, 0, 0, 1, 1, NONE, NWEST, 0.0, 0.0);
SwingUtils.addComponent(bPanel, genCgi, 0, 1, 2, 1, NONE, NWEST, 0.0, 0.0);
- SwingUtils.addComponent(bPanel, genSoap, 0, 2, 2, 1, NONE, NWEST, 0.0, 0.0);
- SwingUtils.addComponent(bPanel, generateBtn, 0, 3, 2, 1, NONE, NWEST, 0.0, 0.0);
+ SwingUtils.addComponent(bPanel, genSoap, 0, 2, 2, 1, NONE, NWEST, 0.0, 0.0);
+ SwingUtils.addComponent(bPanel, generateBtn, 0, 3, 2, 1, HORI, NWEST, 0.0, 0.0);
JPanel uPanel = createTitledPanel("Utilities");
JCheckBox syncServices = createActionBox("Synchronize Services",
@@ -337,21 +461,21 @@
generateBtn.setEnabled(false);
syncBtn.setEnabled(false);
if (syncData) {
- propertyChannel.fire(PM_SYNC_DATATYPES, true);
+ propertyChannel.put(PM_SYNC_DATATYPES, true);
onUpdateDatatypeCache();
}
if (syncServices) {
- propertyChannel.fire(PM_SYNC_SERVICES, true);
+ propertyChannel.put(PM_SYNC_SERVICES, true);
onUpdateServiceCache();
}
}
-
}
});
-
+ syncBtn.setEnabled(enable_moses_actions);
+
SwingUtils.addComponent(uPanel, syncServices, 0, 0, 1, 1, NONE, NWEST, 0.0, 0.0);
SwingUtils.addComponent(uPanel, syncDatatypes, 0, 1, 1, 1, NONE, NWEST, 0.0, 0.0);
- SwingUtils.addComponent(uPanel, syncBtn, 0, 2, 1, 1, NONE, NWEST, 0.0, 0.0);
+ SwingUtils.addComponent(uPanel, syncBtn, 0, 2, 1, 1, HORI, NWEST, 0.0, 0.0);
JPanel ePanel = createTitledPanel("Editor");
@@ -421,14 +545,18 @@
editorCloseButton.setEnabled(false);
editorSaveButton.setEnabled(false);
- SwingUtils.addComponent(ePanel, editorOpenButton, 0, 0, 1, 1, NONE, NWEST, 1.0, 0.0);
+ SwingUtils.addComponent(ePanel, editorOpenButton, 0, 0, 1, 1, NONE, NWEST, 0.0, 0.0);
SwingUtils.addComponent(ePanel, editorSaveButton, 1, 0, 1, 1, NONE, NWEST, 0.0, 0.0);
SwingUtils.addComponent(ePanel, editorCloseButton, 2, 0, 1, 1, NONE, NWEST, 0.0, 0.0);
// add to the main panel
- SwingUtils.addComponent(p, bPanel, 0, 0, 1, 1, HORI, NWEST, 0.0, 0.0);
- SwingUtils.addComponent(p, uPanel, 0, 1, 1, 1, HORI, NWEST, 0.0, 0.0);
- SwingUtils.addComponent(p, ePanel, 0, 2, 1, 1, HORI, NWEST, 0.0, 0.0);
+ int count = 0;
+ if (!enable_moses_actions)
+ SwingUtils.addComponent(p, sPanel, 0, count++, 2, 1, HORI, NWEST, 0.0, 0.0);
+ SwingUtils.addComponent(p, bPanel, 0, count++, 2, 1, HORI, NWEST, 0.0, 0.0);
+ SwingUtils.addComponent(p, uPanel, 0, count++, 2, 1, HORI, NWEST, 0.0, 0.0);
+ SwingUtils.addComponent(p, ePanel, 0, count++, 2, 1, HORI, NWEST, 0.0, 0.0);
+
return p;
}
@@ -677,14 +805,17 @@
try {
// empty the command list
command = new ArrayList<String>();
+ command.addAll(generatePerlPrefix());
+ String s = (command.size() > 0 ? command.remove(command.size()-1) : "") ;
+
// construct the command
- command.add(is_windows_pc ? "moses-generate-datatypes"
- : "moses-generate-datatypes.pl");
+ command.add(is_windows_pc ? "moses-generate-datatypes"
+ : s + "moses-generate-datatypes.pl");
command.add("-R");
command
.add(propertyChannel
.getString(DashboardProperties.DP_REGISTRY_ENDPOINT));
- command.add("-u");
+ command.add("-f");
// place command into com
String[] com = command.toArray(new String[] {});
@@ -727,12 +858,14 @@
propertyChannel
.fire(DP_STATUS_MSG,
"Synchronization of Perl-MoSeS Datatype Cache Complete");
- propertyChannel.fire(PM_SYNC_DATATYPES, new Boolean(false));
+ propertyChannel.put(PM_SYNC_DATATYPES, new Boolean(false));
+
if (!(propertyChannel.getBoolean(PM_SYNC_DATATYPES, false) || propertyChannel
.getBoolean(PM_SYNC_SERVICES, false))) {
syncBtn.setEnabled(true);
generateBtn.setEnabled(true);
}
+
}
};
worker.start();
@@ -752,14 +885,16 @@
try {
// empty the command list
command = new ArrayList<String>();
+ command.addAll(generatePerlPrefix());
+ String s = (command.size() > 0 ? command.remove(command.size()-1) : "") ;
// construct the command
- command.add(is_windows_pc ? "moses-generate-services"
- : "moses-generate-services.pl");
+ command.add(is_windows_pc ? "moses-generate-services"
+ : s + "moses-generate-services.pl");
command.add("-R");
command
.add(propertyChannel
.getString(DashboardProperties.DP_REGISTRY_ENDPOINT));
- command.add("-u");
+ command.add("-f");
// place command into com
String[] com = command.toArray(new String[] {});
@@ -799,11 +934,13 @@
error("Problem executing 'moses-generate-services.pl'",
exception);
console.setEnabledAppendMode(true);
- propertyChannel.fire(PM_SYNC_SERVICES, new Boolean(false));
+ propertyChannel.put(PM_SYNC_SERVICES, new Boolean(false));
propertyChannel.fire(DP_STATUS_MSG,
"Synchronization of Perl-MoSeS Service Cache Complete");
- if (!(propertyChannel.getBoolean(PM_SYNC_DATATYPES, false) || propertyChannel
- .getBoolean(PM_SYNC_SERVICES, false))) {
+ if (!(
+ propertyChannel.getBoolean(PM_SYNC_DATATYPES, false)
+ ||
+ propertyChannel.getBoolean(PM_SYNC_SERVICES, false))) {
syncBtn.setEnabled(true);
generateBtn.setEnabled(true);
}
@@ -827,11 +964,13 @@
ArrayList<String> command = new ArrayList<String>();
// update the moses cache first ...
command = new ArrayList<String>();
+ command.addAll(generatePerlPrefix());
+ String s = (command.size() > 0 ? command.remove(command.size()-1) : "") ;
Process p;
try {
- // construct the command
- command.add(is_windows_pc ? "moses-generate-services"
- : "moses-generate-services.pl");
+ // construct the command
+ command.add(is_windows_pc ? "moses-generate-services"
+ : s + "moses-generate-services.pl");
if (Boolean.parseBoolean(propertyChannel
.getString(PM_OVERWRITE)))
command.add("-F");
@@ -881,7 +1020,7 @@
error("Problem executing 'moses-generate-services.pl'",
exception);
console.setEnabledAppendMode(true);
- propertyChannel.fire(PM_GENERATING_BY_AUTHORITY, new Boolean(
+ propertyChannel.put(PM_GENERATING_BY_AUTHORITY, new Boolean(
false));
if (!(propertyChannel.getBoolean(PM_GENERATING_BY_AUTHORITY, false)|| propertyChannel.getBoolean(PM_GENERATING_SPECIFIC_SERVICES, false))) {
@@ -922,11 +1061,13 @@
+ name + "' provided by '" + auth + "' ...");
console.setEnabledAppendMode(false);
ArrayList<String> command = new ArrayList<String>();
+ command.addAll(generatePerlPrefix());
+ String s = (command.size() > 0 ? command.remove(command.size()-1) : "") ;
Process p;
try {
// construct the command
command.add(is_windows_pc ? "moses-generate-services"
- : "moses-generate-services.pl");
+ : s + "moses-generate-services.pl");
if (Boolean.parseBoolean(propertyChannel
.getString(PM_OVERWRITE)))
command.add("-F");
@@ -979,7 +1120,7 @@
error("Problem executing 'moses-generate-services.pl'",
exception);
console.setEnabledAppendMode(true);
- propertyChannel.fire(PM_GENERATING_SPECIFIC_SERVICES,
+ propertyChannel.put(PM_GENERATING_SPECIFIC_SERVICES,
new Boolean(false));
if (!(propertyChannel.getBoolean(PM_GENERATING_BY_AUTHORITY, false)|| propertyChannel.getBoolean(PM_GENERATING_SPECIFIC_SERVICES, false))) {
@@ -1153,8 +1294,7 @@
Object uo2 = ((DefaultMutableTreeNode) en
.nextElement()).getUserObject();
if (uo2 instanceof CommonNode)
- System.out.println(((CommonNode) uo2)
- .getValue());
+ System.out.println(((CommonNode) uo2).getValue());
}
} else if (nodeType == CommonNode.NODE_SERVICE) {
More information about the MOBY-guts
mailing list