001/*
002 * Copyright (c) 2003-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2015-08-24 22:48:48 +0000 (Mon, 24 Aug 2015) $' 
007 * '$Revision: 33634 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030//implements WmsdActor, which allows Kepler to control the wmsd system (in wmsd.jar at time of writing), which submits Encyclopedia of Life (EOL) tasks via APST system.
031//EOL uses the Grid to predict 3D protein structures on entire genomes from homologous experimentally solved structures in support of rational drug design, ultimately in support of the pharmaceutical industry
032//This extremely CPU-intensive process is considered "embarrassingly parallel" and thus an excellent application for the computational grid.
033//Modeled protein structures are stored in an on-line, Internet accessible database.
034//See more about the Encyclopedia of Life project at SDSC at http://eol.sdsc.edu
035
036package org.eol;
037
038import java.sql.Connection;
039import java.util.ArrayList;
040
041import org.geon.DBConnectionToken;
042
043import edu.sdsc.eol.EolLog;
044import edu.sdsc.eol.Wmsd;
045import ptolemy.actor.TypedAtomicActor;
046import ptolemy.actor.TypedIOPort;
047import ptolemy.data.BooleanToken;
048import ptolemy.data.StringToken;
049import ptolemy.data.expr.FileParameter;
050import ptolemy.data.expr.Parameter;
051import ptolemy.data.expr.StringParameter;
052import ptolemy.data.type.BaseType;
053import ptolemy.kernel.CompositeEntity;
054import ptolemy.kernel.util.IllegalActionException;
055import ptolemy.kernel.util.NameDuplicationException;
056
057/**
058 * Description of the Class
059 */
060public class WmsdActor extends TypedAtomicActor {
061        /**
062         * Construct an actor with the given container and name.
063         * 
064         * @param container
065         *            The container.
066         * @param name
067         *            The name of this actor.
068         * @exception IllegalActionException
069         *                If the actor cannot be contained by the proposed
070         *                container.
071         * @exception NameDuplicationException
072         *                If the container already has an actor with this name.
073         * @since
074         */
075
076        // inputs
077        public TypedIOPort dbcon = new TypedIOPort(this, "dbcon", true, false); // wmsd
078
079        // bookkeeping
080        // database
081
082        public TypedIOPort categoryDatabaseName = new TypedIOPort(this,
083                        "categoryDatabaseName", true, false);
084
085        public TypedIOPort sourceLabel = new TypedIOPort(this, "sourceLabel", true,
086                        false);
087
088        // outputs
089        public TypedIOPort trigger = new TypedIOPort(this, "trigger", false, true);
090
091        public TypedIOPort output_log = new TypedIOPort(this, "output_log", false,
092                        true);
093
094        public Parameter createDB = new Parameter(this, "CREATE_DB",
095                        new BooleanToken(false));
096
097        public Parameter init_apstd = new Parameter(this, "INIT_APSTD",
098                        new BooleanToken(false));
099
100        public Parameter dbPrefix = new StringParameter(this, "DB_PREFIX");
101
102        public Parameter apstdHostColonPort = new StringParameter(this,
103                        "APSTD_HOST_COLON_PORT");
104
105        public FileParameter tmp_dir = new FileParameter(this, "TMP_DIR");
106
107        public Parameter scp_user = new StringParameter(this, "SCP_USER");
108
109        public Parameter scp_server = new StringParameter(this, "SCP_SERVER"); // Null
110
111        // OK
112
113        public Parameter scp_invoke_string = new StringParameter(this,
114                        "SCP_INVOKE_STRING");
115
116        public FileParameter resource_xml_file_name = new FileParameter(this,
117                        "RESOURCE_XML_FILE_NAME");
118
119        public FileParameter application_xml_file_name = new FileParameter(this,
120                        "APPLICATION_XML_FILE_NAME");
121
122        public Parameter enough_tasks = new Parameter(this, "ENOUGH_TASKS");
123
124        public Parameter tasks_per_update = new Parameter(this, "TASKS_PER_UPDATE");
125
126        // Until the DBconnect actor is fixed, when we won't need these anymore.
127        public Parameter db_jdbc_connection_string = new StringParameter(this,
128                        "DB_JDBC_CONNECTION_STRING");
129
130        public Parameter db_username = new StringParameter(this, "DB_USERNAME");
131
132        public Parameter db_password = new StringParameter(this, "DB_PASSWORD");
133
134        private String DB_PREFIX;
135
136        private String APSTD_HOST_COLON_PORT, TMP_DIR, SCP_USER, SCP_SERVER,
137                        SCP_INVOKE_STRING;
138
139        private String RESOURCE_XML_FILE_NAME, APPLICATION_XML_FILE_NAME,
140                        TASKS_PER_UPDATE, ENOUGH_TASKS;
141
142        // Only needed if DBconnection input isn't provided, which happened during
143        // testing because DBconnection was broken, or at least configuration
144        // was broken.
145
146        private String DB_JDBC_CONNECTION_STRING, DB_USERNAME, DB_PASSWORD;
147
148        public WmsdActor(CompositeEntity container, String name)
149                        throws NameDuplicationException, IllegalActionException {
150
151                super(container, name);
152
153                // make these booleans radio buttons
154                createDB.setTypeEquals(BaseType.BOOLEAN);
155                attributeChanged(createDB);
156
157                init_apstd.setTypeEquals(BaseType.BOOLEAN);
158                attributeChanged(init_apstd);
159
160                enough_tasks.setTypeEquals(BaseType.INT);
161                attributeChanged(enough_tasks);
162
163                tasks_per_update.setTypeEquals(BaseType.INT);
164                attributeChanged(tasks_per_update);
165
166                dbcon.setTypeEquals(DBConnectionToken.DBCONNECTION);
167
168                categoryDatabaseName.setTypeEquals(BaseType.STRING);
169                sourceLabel.setTypeEquals(BaseType.STRING);
170
171                trigger.setTypeEquals(BaseType.STRING);
172                output_log.setTypeEquals(BaseType.STRING);
173
174        }
175
176        /**
177         * initialize
178         * 
179         * @throws IllegalActionException
180         */
181        public void initialize() throws IllegalActionException {
182                // WGK: this is the way others do it, but one can imagine
183                // an object that acts as an array encapsulating Parameter,
184                // private String, and string descriptor, and a loop here.
185                // More elegant, less verbose, less prone to bugs.
186
187                DB_JDBC_CONNECTION_STRING = dequoteAndLog(db_jdbc_connection_string,
188                                "DB_JDBC_CONNECTION_STRING");
189                DB_USERNAME = dequoteAndLog(db_username, "DB_USERNAME");
190                DB_PASSWORD = dequoteAndLog(db_password, "DB_PASSWORD");
191                DB_PREFIX = dequoteAndLog(dbPrefix, "DB_PREFIX");
192                APSTD_HOST_COLON_PORT = dequoteAndLog(apstdHostColonPort,
193                                "APSTD_HOST_COLON_PORT");
194                SCP_USER = dequoteAndLog(scp_user, "SCP_USER");
195                SCP_SERVER = dequoteAndLog(scp_server, "SCP_SERVER");
196                SCP_INVOKE_STRING = dequoteAndLog(scp_invoke_string,
197                                "SCP_INVOKE_STRING");
198
199                if (resource_xml_file_name.getToken() != null) {
200                        RESOURCE_XML_FILE_NAME = resource_xml_file_name.asFile().toString();
201                        writeLogLn("RESOURCE_XML_FILE_NAME: " + RESOURCE_XML_FILE_NAME);
202                }
203
204                if (application_xml_file_name.getToken() != null) {
205                        APPLICATION_XML_FILE_NAME = application_xml_file_name.asFile()
206                                        .toString();
207                        writeLogLn("APPLICATION_XML_FILE_NAME: "
208                                        + APPLICATION_XML_FILE_NAME);
209                }
210
211                if (tmp_dir.getToken() != null) {
212                        TMP_DIR = tmp_dir.asFile().toString();
213                        writeLogLn("TMP_DIR: " + TMP_DIR);
214                }
215
216                if (enough_tasks.getToken() != null) {
217                        // Integer type.
218                        ENOUGH_TASKS = enough_tasks.getToken().toString();
219                        writeLogLn("ENOUGH_TASKS: " + ENOUGH_TASKS);
220                }
221
222                if (tasks_per_update.getToken() != null) {
223                        // Integer type.
224                        TASKS_PER_UPDATE = tasks_per_update.getToken().toString();
225                        writeLogLn("TASKS_PER_UPDATE: " + TASKS_PER_UPDATE);
226                }
227
228                if (DB_PREFIX == null || TMP_DIR == null || SCP_USER == null
229                                || RESOURCE_XML_FILE_NAME == null
230                                || APPLICATION_XML_FILE_NAME == null
231                                || SCP_INVOKE_STRING == null || APSTD_HOST_COLON_PORT == null) {
232                        throw new IllegalActionException(
233                                        "The parameters CREATE_DB, DB_PREFIX, APSTD_HOST_COLON_PORT "
234                                                        + "TMP_DIR, SCP_USER, SCP_INVOKE_STRING, INIT_APSTD must have valid values.  CREATE_DB specifies whether to create bookkeeping database tables must be TRUE or FALSE."
235                                                        + "DB_PREFIX gives the wmsd bookkeeping database table prefixes."
236                                                        + "APST_HOST_COLON_PORT is gives the host and port (separated by a colon)"
237                                                        + "where an apstd is listening. (Typically localhost:6660). SCP_INVOKE_STRING provides the local path and command arguments (e.g. identity file) that will be used to start scp. SCP_USER gives the username to be used to scp/ssh login on the apstd host. SCP_SERVER, optional, gives the SCP server when it is different from the apstd connection (due to, say, ssh port forwarding as a result of a firewall.) INIT_APSTD must be TRUE or FALSE, and specifies whether to clear the Apstd and load in the resource file. TMP_DIR gives the name of a temporary directory on the local machine (required)."
238                                                        + "RESOURCE_XML_FILE_NAME must be set to the local location of the XML computation resources file in APSTD format. APPLICATION_XML_FILE_NAME species in the XML file (in WMSD pseudo-Apstd task format) that gives the per-protein and per-genome tasks to be submitted, with variable substitution. ENOUGH_TASKS (optional) specifies the maximum number of tasks apst is allowed to run at any given time --- set larger than cpus in the cluster. TASKS_PER_UPDATE (optional) specifies the max submitted to apstd at any given time. Please provide these values and re-execute.");
239                }
240
241        }
242
243        /**
244         * @return Description of the Returned Value
245         * @exception IllegalActionException
246         *                Description of Exception
247         * @since
248         */
249        public boolean prefire() throws IllegalActionException {
250                return super.prefire();
251        }
252
253        /**
254         * Send a random number with a uniform distribution to the output. This
255         * number is only changed in the prefire() method, so it will remain
256         * constant throughout an iteration.
257         * 
258         * @exception IllegalActionException
259         *                If there is no director.
260         * @since
261         */
262        public void fire() throws IllegalActionException {
263                writeLogLn("firing Wmsd Actor");
264                super.fire();
265
266                // convert inputs to string types
267
268                String categoryDatabaseNameStr = ((StringToken) categoryDatabaseName
269                                .get(0)).stringValue();
270                String sourceLabelStr = ((StringToken) sourceLabel.get(0))
271                                .stringValue();
272
273                ArrayList atemp = new ArrayList();
274
275                // String createDbOption = "--create_db";
276
277                String[] argsTemp = { "--resources", RESOURCE_XML_FILE_NAME,
278                                "--applications", APPLICATION_XML_FILE_NAME, "--apstcontact",
279                                APSTD_HOST_COLON_PORT, "--category", categoryDatabaseNameStr,
280                                "--source", sourceLabelStr, "--dbprefix", DB_PREFIX,
281                                "--tmpdir", TMP_DIR, "--scp_user", SCP_USER,
282                                "--scp_invoke_string", SCP_INVOKE_STRING, };
283                // convert to ArrayList because Java doesn't support dynamic array sizes
284                for (int i = 0; i < argsTemp.length; i++) {
285                        atemp.add(argsTemp[i]);
286                }
287
288                if (((BooleanToken) createDB.getToken()).booleanValue() == true) {
289                        // we've already tested that this is either true or false.
290                        // if it is true, the default value above is used.
291                        atemp.add("--create_db");
292                }
293
294                if (((BooleanToken) init_apstd.getToken()).booleanValue() == true) {
295                        // we've already tested that this is either true or false.
296                        // if it is true, the default value above is used.
297                        atemp.add("--init_apstd");
298                }
299
300                if (SCP_SERVER != null && !SCP_SERVER.equals("")) {
301                        atemp.add("--scp_server");
302                        atemp.add(SCP_SERVER);
303                }
304
305                if (ENOUGH_TASKS != null) {
306                        ENOUGH_TASKS = (new Integer(ENOUGH_TASKS)).toString(); // make sure
307                        // integer
308                        // value.
309                        atemp.add("--enough_tasks");
310                        atemp.add(ENOUGH_TASKS);
311                }
312
313                if (TASKS_PER_UPDATE != null) {
314                        TASKS_PER_UPDATE = (new Integer(TASKS_PER_UPDATE)).toString(); // make
315                        // sure
316                        // integer
317                        // value.
318                        atemp.add("--tasks_per_update");
319                        atemp.add(TASKS_PER_UPDATE);
320                }
321
322                try {
323                        Wmsd wm = null;
324                        if (dbcon.numberOfSources() > 0 && dbcon.hasToken(0)) {
325                                String[] args = (String[]) atemp.toArray(new String[atemp
326                                                .size()]);
327
328                                DBConnectionToken _dbcon = (DBConnectionToken) dbcon.get(0);
329                                Connection _con;
330                                try {
331                                        _con = _dbcon.getValue();
332
333                                } catch (Exception e) {
334                                        throw new IllegalActionException(this, e,
335                                                        "CONNECTION FAILURE");
336                                }
337
338                                wm = new Wmsd(args, _con);
339
340                        } else {
341                                // DB connect broken, user putting in db parameters manually.
342                                // :-(
343                                // this stuff will be eliminated once DBconnect actor is again
344                                // working reliably.
345
346                                if (DB_JDBC_CONNECTION_STRING == null || DB_USERNAME == null
347                                                || DB_PASSWORD == null) {
348                                        throw new IllegalActionException(
349                                                        "In the absence of an DBconnect input, DB_JDBC_CONNECTION_STRING, DB_USERNAME, DB_PASSWORD must not be null but provide the db connection parameters for the Wmsd bookkeeping database.");
350                                }
351                                atemp.add("--dbhost");
352                                atemp.add(DB_JDBC_CONNECTION_STRING);
353                                atemp.add("--dbuser");
354                                atemp.add(DB_USERNAME);
355                                atemp.add("--dbpassword");
356                                atemp.add(DB_PASSWORD);
357
358                                String[] args = (String[]) atemp.toArray(new String[atemp
359                                                .size()]);
360                                wm = new Wmsd(args);
361                        }
362                        wm.run();
363
364                } catch (Exception e) {
365                        e.printStackTrace();
366                        throw new IllegalActionException("Unexpected error: "
367                                        + e.getMessage());
368                }
369
370                copyToLog(EolLog.getAndClearOutData());
371                broadcastToLog();
372
373                trigger.broadcast(new StringToken(categoryDatabaseNameStr));
374                writeLogLn("Wmsd Actor done");
375
376        }
377
378        private void writeLogLn(String msg) {
379                System.out.println(msg);
380                copyToLog(msg + "\n");
381        }
382
383        private String logString = null;
384
385        private void copyToLog(String msg) {
386                if (logString == null) {
387                        logString = new String();
388                }
389                logString += msg;
390        }
391
392        private void broadcastToLog() {
393                if (logString != null) {
394                        try {
395                                output_log.broadcast(new StringToken(logString));
396                        } catch (IllegalActionException e) {
397                                e.printStackTrace();
398                        }
399                }
400        }
401
402        private String dequote(String mystring) {
403                return (mystring.substring(1, mystring.length() - 1));
404        }
405
406        private String dequoteAndLog(String mystring, String name) {
407                mystring = dequote(mystring);
408                writeLogLn(name + ": " + mystring);
409                return (mystring);
410        }
411
412        private String dequoteAndLog(Parameter myparm, String name)
413                        throws IllegalActionException {
414                if (myparm.getToken() != null) {
415                        // only fix and print if not equal to null.
416                        String mystring = ((StringToken) myparm.getToken()).toString();
417                        return (dequoteAndLog(mystring, name));
418                }
419                return (null);
420        }
421
422}