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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2009 Sun Microsystems, Inc. 025 * Portions Copyright 2013-2015 ForgeRock AS 026 */ 027package org.opends.server.tools; 028 029import java.io.*; 030 031import org.forgerock.i18n.LocalizableMessage; 032import org.opends.server.core.DirectoryServer; 033import org.opends.server.loggers.JDKLogging; 034import org.opends.server.types.NullOutputStream; 035 036import com.forgerock.opendj.cli.*; 037 038import static org.opends.messages.CoreMessages.*; 039import static org.opends.messages.ToolMessages.*; 040import static org.opends.server.util.StaticUtils.*; 041import static com.forgerock.opendj.cli.Utils.filterExitCode; 042 043/** 044 * This program provides a simple tool that will wait for a specified file to be 045 * deleted before exiting. It can be used in the process of confirming that the 046 * server has completed its startup or shutdown process. 047 */ 048public class WaitForFileDelete extends ConsoleApplication 049{ 050 /** 051 * The fully-qualified name of this class. 052 */ 053 private static final String CLASS_NAME = 054 "org.opends.server.tools.WaitForFileDelete"; 055 056 057 058 /** 059 * The exit code value that will be used if the target file is deleted 060 * successfully. 061 */ 062 public static final int EXIT_CODE_SUCCESS = 0; 063 064 065 066 /** 067 * The exit code value that will be used if an internal error occurs within 068 * this program. 069 */ 070 public static final int EXIT_CODE_INTERNAL_ERROR = 1; 071 072 073 074 /** 075 * The exit code value that will be used if a timeout occurs while waiting for 076 * the file to be removed. 077 */ 078 public static final int EXIT_CODE_TIMEOUT = 2; 079 080 081 082 /** 083 * Constructor for the WaitForFileDelete object. 084 * 085 * @param out the print stream to use for standard output. 086 * @param err the print stream to use for standard error. 087 * @param in the input stream to use for standard input. 088 */ 089 public WaitForFileDelete(PrintStream out, PrintStream err, InputStream in) 090 { 091 super(out, err); 092 } 093 094 /** 095 * Processes the command-line arguments and initiates the process of waiting 096 * for the file to be removed. 097 * 098 * @param args The command-line arguments provided to this program. 099 */ 100 public static void main(String[] args) 101 { 102 int retCode = mainCLI(args, true, System.out, System.err, System.in); 103 104 System.exit(retCode); 105 } 106 107 /** 108 * Processes the command-line arguments and initiates the process of waiting 109 * for the file to be removed. 110 * 111 * @param args The command-line arguments provided to this 112 * program. 113 * @param initializeServer Indicates whether to initialize the server. 114 * @param outStream The output stream to use for standard output, or 115 * <CODE>null</CODE> if standard output is not 116 * needed. 117 * @param errStream The output stream to use for standard error, or 118 * <CODE>null</CODE> if standard error is not 119 * needed. 120 * @param inStream The input stream to use for standard input. 121 * @return The error code. 122 */ 123 124 public static int mainCLI(String[] args, boolean initializeServer, 125 OutputStream outStream, OutputStream errStream, InputStream inStream) 126 { 127 int exitCode; 128 PrintStream out = NullOutputStream.wrapOrNullStream(outStream); 129 PrintStream err = NullOutputStream.wrapOrNullStream(errStream); 130 JDKLogging.disableLogging(); 131 try 132 { 133 WaitForFileDelete wffd = new WaitForFileDelete(out, err, System.in); 134 exitCode = wffd.mainWait(args); 135 if (exitCode != EXIT_CODE_SUCCESS) 136 { 137 exitCode = filterExitCode(exitCode); 138 } 139 } 140 catch (Exception e) 141 { 142 e.printStackTrace(); 143 exitCode = EXIT_CODE_INTERNAL_ERROR; 144 } 145 return exitCode; 146 } 147 148 149 150 /** 151 * Processes the command-line arguments and then waits for the specified file 152 * to be removed. 153 * 154 * @param args The command-line arguments provided to this program. 155 * @param out The output stream to use for standard output, or 156 * <CODE>null</CODE> if standard output is not 157 * needed. 158 * @param err The output stream to use for standard error, or 159 * <CODE>null</CODE> if standard error is not 160 * needed. 161 * @param inStream The input stream to use for standard input. 162 * 163 * @return An integer value of zero if the file was deleted successfully, or 164 * some other value if a problem occurred. 165 */ 166 private int mainWait(String[] args) 167 { 168 // Create all of the command-line arguments for this program. 169 BooleanArgument showUsage = null; 170 IntegerArgument timeout = null; 171 StringArgument logFilePath = null; 172 StringArgument targetFilePath = null; 173 StringArgument outputFilePath = null; 174 BooleanArgument useLastKnownGoodConfig = null; 175 BooleanArgument quietMode = null; 176 177 LocalizableMessage toolDescription = INFO_WAIT4DEL_TOOL_DESCRIPTION.get(); 178 ArgumentParser argParser = new ArgumentParser(CLASS_NAME, toolDescription, 179 false); 180 181 try 182 { 183 targetFilePath = 184 new StringArgument("targetfile", 'f', "targetFile", true, false, 185 true, INFO_PATH_PLACEHOLDER.get(), null, null, 186 INFO_WAIT4DEL_DESCRIPTION_TARGET_FILE.get()); 187 argParser.addArgument(targetFilePath); 188 189 190 logFilePath = new StringArgument( 191 "logfile", 'l', "logFile", false, false, 192 true, INFO_PATH_PLACEHOLDER.get(), null, null, 193 INFO_WAIT4DEL_DESCRIPTION_LOG_FILE.get()); 194 argParser.addArgument(logFilePath); 195 196 197 outputFilePath = new StringArgument( 198 "outputfile", 'o', "outputFile", 199 false, false, 200 true, INFO_PATH_PLACEHOLDER.get(), null, null, 201 INFO_WAIT4DEL_DESCRIPTION_OUTPUT_FILE.get()); 202 argParser.addArgument(outputFilePath); 203 204 205 timeout = new IntegerArgument("timeout", 't', "timeout", true, false, 206 true, INFO_SECONDS_PLACEHOLDER.get(), 207 DirectoryServer.DEFAULT_TIMEOUT, 208 null, true, 0, false, 209 0, INFO_WAIT4DEL_DESCRIPTION_TIMEOUT.get()); 210 argParser.addArgument(timeout); 211 212 213 // Not used in this class, but required by the start-ds script 214 // (see issue #3814) 215 useLastKnownGoodConfig = 216 new BooleanArgument("lastknowngoodconfig", 'L', 217 "useLastKnownGoodConfig", 218 INFO_DSCORE_DESCRIPTION_LASTKNOWNGOODCFG.get()); 219 argParser.addArgument(useLastKnownGoodConfig); 220 221 // Not used in this class, but required by the start-ds script 222 // (see issue #3814) 223 quietMode = CommonArguments.getQuiet(); 224 argParser.addArgument(quietMode); 225 226 showUsage = CommonArguments.getShowUsage(); 227 argParser.addArgument(showUsage); 228 argParser.setUsageArgument(showUsage); 229 } 230 catch (ArgumentException ae) 231 { 232 LocalizableMessage message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()); 233 println(message); 234 return EXIT_CODE_INTERNAL_ERROR; 235 } 236 237 238 // Parse the command-line arguments provided to the program. 239 try 240 { 241 argParser.parseArguments(args); 242 } 243 catch (ArgumentException ae) 244 { 245 argParser.displayMessageAndUsageReference(getErrStream(), ERR_ERROR_PARSING_ARGS.get(ae.getMessage())); 246 return EXIT_CODE_INTERNAL_ERROR; 247 } 248 249 250 // If we should just display usage or version information, 251 // then print it and exit. 252 if (argParser.usageOrVersionDisplayed()) 253 { 254 return EXIT_CODE_SUCCESS; 255 } 256 257 258 // Get the file to watch. If it doesn't exist now, then exit immediately. 259 File targetFile = new File(targetFilePath.getValue()); 260 if (! targetFile.exists()) 261 { 262 return EXIT_CODE_SUCCESS; 263 } 264 265 266 // If a log file was specified, then open it. 267 long logFileOffset = 0L; 268 RandomAccessFile logFile = null; 269 if (logFilePath.isPresent()) 270 { 271 try 272 { 273 File f = new File(logFilePath.getValue()); 274 if (f.exists()) 275 { 276 logFile = new RandomAccessFile(f, "r"); 277 logFileOffset = logFile.length(); 278 logFile.seek(logFileOffset); 279 } 280 } 281 catch (Exception e) 282 { 283 println(WARN_WAIT4DEL_CANNOT_OPEN_LOG_FILE.get(logFilePath.getValue(), e)); 284 logFile = null; 285 } 286 } 287 288 289 // If an output file was specified and we could open the log file, open it 290 // and append data to it. 291 RandomAccessFile outputFile = null; 292 long outputFileOffset = 0L; 293 if (logFile != null && outputFilePath.isPresent()) 294 { 295 try 296 { 297 File f = new File(outputFilePath.getValue()); 298 if (f.exists()) 299 { 300 outputFile = new RandomAccessFile(f, "rw"); 301 outputFileOffset = outputFile.length(); 302 outputFile.seek(outputFileOffset); 303 } 304 } 305 catch (Exception e) 306 { 307 println(WARN_WAIT4DEL_CANNOT_OPEN_OUTPUT_FILE.get(outputFilePath.getValue(), e)); 308 outputFile = null; 309 } 310 } 311 // Figure out when to stop waiting. 312 long stopWaitingTime; 313 try 314 { 315 long timeoutMillis = 1000L * Integer.parseInt(timeout.getValue()); 316 if (timeoutMillis > 0) 317 { 318 stopWaitingTime = System.currentTimeMillis() + timeoutMillis; 319 } 320 else 321 { 322 stopWaitingTime = Long.MAX_VALUE; 323 } 324 } 325 catch (Exception e) 326 { 327 // This shouldn't happen, but if it does then ignore it. 328 stopWaitingTime = System.currentTimeMillis() + 60000; 329 } 330 331 332 // Operate in a loop, printing out any applicable log messages and waiting 333 // for the target file to be removed. 334 byte[] logBuffer = new byte[8192]; 335 while (System.currentTimeMillis() < stopWaitingTime) 336 { 337 if (logFile != null) 338 { 339 try 340 { 341 while (logFile.length() > logFileOffset) 342 { 343 int bytesRead = logFile.read(logBuffer); 344 if (bytesRead > 0) 345 { 346 if (outputFile == null) 347 { 348 getOutputStream().write(logBuffer, 0, bytesRead); 349 getOutputStream().flush(); 350 } 351 else 352 { 353 // Write on the file. 354 // TODO 355 outputFile.write(logBuffer, 0, bytesRead); 356 357 } 358 logFileOffset += bytesRead; 359 } 360 } 361 } 362 catch (Exception e) 363 { 364 // We'll just ignore this. 365 } 366 } 367 368 369 if (! targetFile.exists()) 370 { 371 break; 372 } 373 else 374 { 375 try 376 { 377 Thread.sleep(10); 378 } catch (InterruptedException ie) {} 379 } 380 } 381 382 close(outputFile); 383 384 if (targetFile.exists()) 385 { 386 println(ERR_TIMEOUT_DURING_STARTUP.get( 387 Integer.parseInt(timeout.getValue()), 388 timeout.getLongIdentifier())); 389 return EXIT_CODE_TIMEOUT; 390 } 391 else 392 { 393 return EXIT_CODE_SUCCESS; 394 } 395 } 396 397 /** {@inheritDoc} */ 398 @Override 399 public boolean isAdvancedMode() 400 { 401 return false; 402 } 403 404 /** {@inheritDoc} */ 405 @Override 406 public boolean isInteractive() 407 { 408 return false; 409 } 410 411 /** {@inheritDoc} */ 412 @Override 413 public boolean isMenuDrivenMode() 414 { 415 return false; 416 } 417 418 /** {@inheritDoc} */ 419 @Override 420 public boolean isQuiet() 421 { 422 return false; 423 } 424 425 /** {@inheritDoc} */ 426 @Override 427 public boolean isScriptFriendly() 428 { 429 return false; 430 } 431 432 /** {@inheritDoc} */ 433 @Override 434 public boolean isVerbose() 435 { 436 return false; 437 } 438} 439