001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021package org.biojava.nbio.structure.align;
022
023import org.biojava.nbio.structure.align.client.FarmJobParameters;
024import org.biojava.nbio.structure.align.client.FarmJobRunnable;
025import org.biojava.nbio.structure.align.events.AlignmentProgressListener;
026import org.biojava.nbio.structure.align.util.CliTools;
027import org.biojava.nbio.structure.align.util.ConfigurationException;
028import org.biojava.nbio.structure.align.util.UserConfiguration;
029import org.biojava.nbio.structure.scop.CachedRemoteScopInstallation;
030import org.biojava.nbio.structure.scop.ScopDatabase;
031import org.biojava.nbio.structure.scop.ScopFactory;
032import org.biojava.nbio.core.util.InputStreamProvider;
033import org.slf4j.Logger;
034import org.slf4j.LoggerFactory;
035
036import java.io.IOException;
037import java.util.ArrayList;
038import java.util.Arrays;
039import java.util.List;
040
041
042/** A job as it can be run on the farm.
043 *
044 * @author Andreas Prlic
045 *
046 * for arguments see the printHelp() method.
047 *
048 *
049 *
050 */
051public class FarmJob implements Runnable {
052
053        private final static Logger logger = LoggerFactory.getLogger(FarmJob.class);
054
055        private static final String[] mandParams = new String[] {"pdbFilePath"};
056
057        private static final List<String> mandatoryArgs= Arrays.asList(mandParams);
058
059        List<AlignmentProgressListener> progressListeners;
060        List<FarmJobRunnable> jobs;
061
062        FarmJobParameters params ;
063
064        public FarmJob(){
065                progressListeners = null;
066
067                // send a flag to the PDb file loader to cache the gzip compressed files.
068                System.setProperty(InputStreamProvider.CACHE_PROPERTY, "true");
069
070
071        }
072
073        public FarmJobParameters getParams() {
074                return params;
075        }
076
077        public void setParams(FarmJobParameters params) {
078                this.params = params;
079        }
080
081        public void addAlignmentProgressListener(AlignmentProgressListener listener){
082                if (progressListeners == null)
083                        progressListeners = new ArrayList<AlignmentProgressListener>();
084
085                progressListeners.add(listener);
086        }
087
088        public void clearListeners(){
089                progressListeners.clear();
090                progressListeners = null;
091        }
092
093        public static void main(String[] argv){
094
095                FarmJob job = new FarmJob();
096
097                if (argv.length  == 0 ) {
098                        job.printHelp();
099                        return;
100                }
101
102                if ( argv.length == 1){
103                        if (argv[0].equalsIgnoreCase("-h") || argv[0].equalsIgnoreCase("-help")|| argv[0].equalsIgnoreCase("--help")){
104                                job.printHelp();
105                                return;
106                        }
107                }
108
109                FarmJobParameters params = new FarmJobParameters();
110
111                for (int i = 0 ; i < argv.length; i++){
112                        String arg   = argv[i];
113
114                        String value = null;
115                        if ( i < argv.length -1)
116                                value = argv[i+1];
117
118                        // if value starts with - then the arg does not have a value.
119                        if (value != null && value.startsWith("-"))
120                                value = null;
121                        else
122                                i++;
123
124
125                        String[] tmp = {arg,value};
126
127                        try {
128
129                                CliTools.configureBean(params, tmp);
130
131                        } catch (ConfigurationException e){
132
133                                logger.error("Exception", e);
134
135                                if ( mandatoryArgs.contains(arg) ) {
136                                        // there must not be a ConfigurationException with mandatory arguments.
137                                        return;
138                                } else {
139                                        // but there can be with optional ...
140                                }
141                        }
142                }
143
144
145                if (( params.getNrAlignments() == -1) && (params.getTime() == -1)){
146                        logger.error("Please provide either the -time or the -nrAlignments argument!");
147                        return;
148                }
149
150
151                logger.info("Using parameters: {}", params);
152
153                job.setParams(params);
154                job.run();
155
156        }
157
158        @Override
159        public void run(){
160
161
162                // set the system wide PDB path
163
164                String path = params.getPdbFilePath();
165                System.setProperty(UserConfiguration.PDB_DIR,path);
166
167                String cachePath = params.getCacheFilePath();
168                if ( cachePath != null && ! cachePath.equals(""))
169                        System.setProperty(UserConfiguration.PDB_CACHE_DIR,cachePath);
170                else {
171                        // if not provided, we use pdbFilePath as the default CACHE path
172                        System.setProperty(UserConfiguration.PDB_CACHE_DIR,path);
173                }
174                // declare SCOP to be locally cached, but fetching new stuff from remote
175                ScopDatabase scop = null;
176                try {
177                        scop = new CachedRemoteScopInstallation(true);
178                } catch (IOException e) {
179                        throw new RuntimeException("Could not load " + CachedRemoteScopInstallation.class.getName(), e);
180                }
181                ScopFactory.setScopDatabase(scop);
182
183                String username = params.getUsername();
184                jobs = new ArrayList<FarmJobRunnable>();
185                for ( int i = 0 ; i < params.getThreads();i++){
186                        logger.info("starting thread #{}", (i+1));
187                        FarmJobRunnable runner = new FarmJobRunnable(params);
188                        params.setUsername(username+"_thread_" + (i+1));
189                        jobs.add(runner);
190
191                        if ( progressListeners != null) {
192                                for (AlignmentProgressListener li : progressListeners){
193                                        runner.addAlignmentProgressListener(li);
194                                }
195                        }
196
197
198                        Thread t = new Thread(runner);
199                        if ( ( (params.getThreads() > 1 ) && ( i < params.getThreads() - 1) )|| ( params.isRunBackground())) {
200                                logger.info("starting thread #{} in background.", (i + 1));
201                                t.start();
202                        } else {
203                                // single CPU jobs are run in the main thread and the last job is also run in the main thread
204                                logger.info("starting thread #{} in main thread.", (i + 1));
205                                t.run();
206                        }
207                }
208        }
209
210        public void terminate(){
211
212                logger.info("terminating jobs");
213
214                if ( jobs == null)
215                        return;
216
217                int js = jobs.size();
218                logger.info("number of jobs: {}", js);
219
220
221                for (FarmJobRunnable runner : jobs){
222                        // runner.terminate() is already synchronized
223                        runner.terminate();
224                }
225
226                clearListeners();
227        }
228
229        public void printHelp(){
230                System.out.println("-------------------");
231                System.out.println("FarmJob help:");
232                System.out.println("-------------------");
233
234                System.out.println("FarmJob accepts the following parameters:");
235                System.out.println("");
236                System.out.println(" Mandatory:");
237                System.out.println("   -pdbFilePath (mandatory) Path to the directory in your file system that contains the PDB files.");
238
239                System.out.println("   provide either -time or -nrAlignments. If both are provided the job stops as soon as any of the criteria has been reached.");
240                System.out.println("   -time maximum number of time to run (in seconds). -1 means no time limit, but run -nrAlignment arguments. Default: " + FarmJobParameters.DEFAULT_JOB_TIME );
241                System.out.println("   -nrAlignments number of alignments to calculate. Default: " + FarmJobParameters.DEFAULT_NR_ALIGNMENTS) ;
242                System.out.println("");
243                System.out.println(" Optional: ");
244                System.out.println("   -threads number of parallel threads to calculate alignments. Should be nr. of available CPUs. Default: " + FarmJobParameters.DEFAULT_NR_THREADS);
245                System.out.println("   -server the location of the server URL to talk to. Default : " + FarmJobParameters.DEFAULT_SERVER_URL);
246                System.out.println("   -username a unique name that can be given to this client. Can be used to give credit for who is doing the calculations. Default: IP and a random id");
247                System.out.println("   -stepSize the number of pairs to be requsted from server. Default: " + FarmJobParameters.DEFAULT_BATCH_SIZE);
248        }
249}