001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.tools;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.io.File;
033    import java.io.RandomAccessFile;
034    
035    import org.opends.server.util.args.ArgumentException;
036    import org.opends.server.util.args.ArgumentParser;
037    import org.opends.server.util.args.BooleanArgument;
038    import org.opends.server.util.args.IntegerArgument;
039    import org.opends.server.util.args.StringArgument;
040    
041    import static org.opends.messages.ToolMessages.*;
042    import static org.opends.server.util.ServerConstants.*;
043    import static org.opends.server.util.StaticUtils.*;
044    
045    
046    
047    /**
048     * This program provides a simple tool that will wait for a specified file to be
049     * deleted before exiting.  It can be used in the process of confirming that the
050     * server has completed its startup or shutdown process.
051     */
052    public class WaitForFileDelete
053    {
054      /**
055       * The fully-qualified name of this class.
056       */
057      private static final String CLASS_NAME =
058           "org.opends.server.tools.WaitForFileDelete";
059    
060    
061    
062      /**
063       * The exit code value that will be used if the target file is deleted
064       * successfully.
065       */
066      public static final int EXIT_CODE_SUCCESS = 0;
067    
068    
069    
070      /**
071       * The exit code value that will be used if an internal error occurs within
072       * this program.
073       */
074      public static final int EXIT_CODE_INTERNAL_ERROR = 1;
075    
076    
077    
078      /**
079       * The exit code value that will be used if a timeout occurs while waiting for
080       * the file to be removed.
081       */
082      public static final int EXIT_CODE_TIMEOUT = 2;
083    
084    
085    
086      /**
087       * Processes the command-line arguments and initiates the process of waiting
088       * for the file to be removed.
089       *
090       * @param  args  The command-line arguments provided to this program.
091       */
092      public static void main(String[] args)
093      {
094        try
095        {
096          int exitCode = mainWait(args);
097          if (exitCode != EXIT_CODE_SUCCESS)
098          {
099            System.exit(filterExitCode(exitCode));
100          }
101        }
102        catch (Exception e)
103        {
104          e.printStackTrace();
105          System.exit(EXIT_CODE_INTERNAL_ERROR);
106        }
107      }
108    
109    
110    
111      /**
112       * Processes the command-line arguments and then waits for the specified file
113       * to be removed.
114       *
115       * @param  args  The command-line arguments provided to this program.
116       *
117       * @return  An integer value of zero if the file was deleted successfully, or
118       *          some other value if a problem occurred.
119       */
120      public static int mainWait(String[] args)
121      {
122        // Create all of the command-line arguments for this program.
123        BooleanArgument showUsage      = null;
124        IntegerArgument timeout        = null;
125        StringArgument  logFilePath    = null;
126        StringArgument  targetFilePath = null;
127        StringArgument  outputFilePath = null;
128    
129        Message toolDescription = INFO_WAIT4DEL_TOOL_DESCRIPTION.get();
130        ArgumentParser argParser = new ArgumentParser(CLASS_NAME, toolDescription,
131                                                      false);
132    
133        try
134        {
135          targetFilePath =
136               new StringArgument("targetfile", 'f', "targetFile", true, false,
137                                  true, INFO_PATH_PLACEHOLDER.get(), null, null,
138                                  INFO_WAIT4DEL_DESCRIPTION_TARGET_FILE.get());
139          argParser.addArgument(targetFilePath);
140    
141    
142          logFilePath = new StringArgument(
143                  "logfile", 'l', "logFile", false, false,
144                  true, INFO_PATH_PLACEHOLDER.get(), null, null,
145                  INFO_WAIT4DEL_DESCRIPTION_LOG_FILE.get());
146          argParser.addArgument(logFilePath);
147    
148    
149          outputFilePath = new StringArgument(
150                  "outputfile", 'o', "outputFile",
151                  false, false,
152                  true, INFO_PATH_PLACEHOLDER.get(), null, null,
153                  INFO_WAIT4DEL_DESCRIPTION_OUTPUT_FILE.get());
154          argParser.addArgument(outputFilePath);
155    
156    
157          timeout = new IntegerArgument("timeout", 't', "timeout", true, false,
158                                        true, INFO_SECONDS_PLACEHOLDER.get(), 60,
159                                        null, true, 0, false,
160                                        0, INFO_WAIT4DEL_DESCRIPTION_TIMEOUT.get());
161          argParser.addArgument(timeout);
162    
163    
164          showUsage = new BooleanArgument("help", 'H', "help",
165                                          INFO_WAIT4DEL_DESCRIPTION_HELP.get());
166          argParser.addArgument(showUsage);
167          argParser.setUsageArgument(showUsage);
168        }
169        catch (ArgumentException ae)
170        {
171          Message message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
172          System.err.println(wrapText(message, MAX_LINE_WIDTH));
173          return EXIT_CODE_INTERNAL_ERROR;
174        }
175    
176    
177        // Parse the command-line arguments provided to the program.
178        try
179        {
180          argParser.parseArguments(args);
181        }
182        catch (ArgumentException ae)
183        {
184          Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage());
185    
186          System.err.println(wrapText(message, MAX_LINE_WIDTH));
187          System.err.println(argParser.getUsage());
188          return EXIT_CODE_INTERNAL_ERROR;
189        }
190    
191    
192        // If we should just display usage or version information,
193        // then print it and exit.
194        if (argParser.usageOrVersionDisplayed())
195        {
196          return EXIT_CODE_SUCCESS;
197        }
198    
199    
200        // Get the file to watch.  If it doesn't exist now, then exit immediately.
201        File targetFile = new File(targetFilePath.getValue());
202        if (! targetFile.exists())
203        {
204          return EXIT_CODE_SUCCESS;
205        }
206    
207    
208        // If a log file was specified, then open it.
209        long logFileOffset = 0L;
210        RandomAccessFile logFile = null;
211        if (logFilePath.isPresent())
212        {
213          try
214          {
215            File f = new File(logFilePath.getValue());
216            if (f.exists())
217            {
218              logFile = new RandomAccessFile(f, "r");
219              logFileOffset = logFile.length();
220              logFile.seek(logFileOffset);
221            }
222          }
223          catch (Exception e)
224          {
225            Message message = WARN_WAIT4DEL_CANNOT_OPEN_LOG_FILE.get(
226                    logFilePath.getValue(), String.valueOf(e));
227            System.err.println(wrapText(message, MAX_LINE_WIDTH));
228    
229            logFile = null;
230          }
231        }
232    
233    
234        // If an output file was specified and we could open the log file, open it
235        // and append data to it.
236        RandomAccessFile outputFile = null;
237        long outputFileOffset = 0L;
238        if (logFile != null)
239        {
240          if (outputFilePath.isPresent())
241          {
242            try
243            {
244              File f = new File(outputFilePath.getValue());
245              if (f.exists())
246              {
247                outputFile = new RandomAccessFile(f, "rw");
248                outputFileOffset = outputFile.length();
249                outputFile.seek(outputFileOffset);
250              }
251            }
252            catch (Exception e)
253            {
254              Message message = WARN_WAIT4DEL_CANNOT_OPEN_OUTPUT_FILE.get(
255                      outputFilePath.getValue(), String.valueOf(e));
256              System.err.println(wrapText(message, MAX_LINE_WIDTH));
257    
258              outputFile = null;
259            }
260          }
261        }
262        // Figure out when to stop waiting.
263        long stopWaitingTime;
264        try
265        {
266          long timeoutMillis = 1000L * Integer.parseInt(timeout.getValue());
267          if (timeoutMillis > 0)
268          {
269            stopWaitingTime = System.currentTimeMillis() + timeoutMillis;
270          }
271          else
272          {
273            stopWaitingTime = Long.MAX_VALUE;
274          }
275        }
276        catch (Exception e)
277        {
278          // This shouldn't happen, but if it does then ignore it.
279          stopWaitingTime = System.currentTimeMillis() + 60000;
280        }
281    
282    
283        // Operate in a loop, printing out any applicable log messages and waiting
284        // for the target file to be removed.
285        byte[] logBuffer = new byte[8192];
286        while (System.currentTimeMillis() < stopWaitingTime)
287        {
288          if (logFile != null)
289          {
290            try
291            {
292              while (logFile.length() > logFileOffset)
293              {
294                int bytesRead = logFile.read(logBuffer);
295                if (bytesRead > 0)
296                {
297                  if (outputFile == null)
298                  {
299                    System.out.write(logBuffer, 0, bytesRead);
300                    System.out.flush();
301                  }
302                  else
303                  {
304                    // Write on the file.
305                    // TODO
306                    outputFile.write(logBuffer, 0, bytesRead);
307    
308                  }
309                  logFileOffset += bytesRead;
310                }
311              }
312            }
313            catch (Exception e)
314            {
315              // We'll just ignore this.
316            }
317          }
318    
319    
320          if (! targetFile.exists())
321          {
322            break;
323          }
324          else
325          {
326            try
327            {
328              Thread.sleep(10);
329            } catch (InterruptedException ie) {}
330          }
331        }
332    
333        if (outputFile != null)
334        {
335          try
336          {
337            outputFile.close();
338          }
339          catch (Throwable t) {}
340        }
341    
342        if (targetFile.exists())
343        {
344          return EXIT_CODE_TIMEOUT;
345        }
346        else
347        {
348          return EXIT_CODE_SUCCESS;
349        }
350      }
351    }
352