1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.harmony.logging.tests.java.util.logging; 19 20 import java.io.BufferedInputStream; 21 import java.io.ByteArrayOutputStream; 22 import java.io.File; 23 import java.io.FileInputStream; 24 import java.io.FileNotFoundException; 25 import java.io.FileOutputStream; 26 import java.io.FilePermission; 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.io.OutputStream; 30 import java.io.PrintStream; 31 import java.io.StringWriter; 32 import java.security.Permission; 33 import java.util.Properties; 34 import java.util.logging.FileHandler; 35 import java.util.logging.Filter; 36 import java.util.logging.Formatter; 37 import java.util.logging.Handler; 38 import java.util.logging.Level; 39 import java.util.logging.LogManager; 40 import java.util.logging.LogRecord; 41 import java.util.logging.LoggingPermission; 42 import java.util.logging.XMLFormatter; 43 44 import junit.framework.TestCase; 45 46 import org.apache.harmony.logging.tests.java.util.logging.HandlerTest.NullOutputStream; 47 import org.apache.harmony.logging.tests.java.util.logging.util.EnvironmentHelper; 48 49 /** 50 */ 51 public class FileHandlerTest extends TestCase { 52 53 static LogManager manager = LogManager.getLogManager(); 54 55 final static Properties props = new Properties(); 56 57 final static String className = FileHandlerTest.class.getName(); 58 59 final static StringWriter writer = new StringWriter(); 60 61 final static String USR_HOME_KEY = "user.home"; 62 63 final static String TMP_DIR_KEY = "java.io.tmpdir"; 64 65 final static String HOMEPATH = System.getProperty(USR_HOME_KEY); 66 67 final static String TEMPPATH = System.getProperty(TMP_DIR_KEY); 68 69 final static String SEP = File.separator; 70 71 private final PrintStream err = System.err; 72 73 private OutputStream errSubstituteStream = null; 74 75 FileHandler handler; 76 77 LogRecord r; 78 79 /* 80 * @see TestCase#setUp() 81 */ setUp()82 protected void setUp() throws Exception { 83 super.setUp(); 84 manager.reset(); 85 initProps(); 86 File file = new File(TEMPPATH + SEP + "log"); 87 file.mkdir(); 88 manager.readConfiguration(EnvironmentHelper 89 .PropertiesToInputStream(props)); 90 handler = new FileHandler(); 91 r = new LogRecord(Level.CONFIG, "msg"); 92 errSubstituteStream = new NullOutputStream(); 93 System.setErr(new PrintStream(errSubstituteStream)); 94 } 95 96 /** 97 * 98 */ initProps()99 private void initProps() { 100 props.clear(); 101 props.put("java.util.logging.FileHandler.level", "FINE"); 102 props.put("java.util.logging.FileHandler.filter", className 103 + "$MockFilter"); 104 props.put("java.util.logging.FileHandler.formatter", className 105 + "$MockFormatter"); 106 props.put("java.util.logging.FileHandler.encoding", "iso-8859-1"); 107 // limit to only two message 108 props.put("java.util.logging.FileHandler.limit", "1000"); 109 // rotation count is 2 110 props.put("java.util.logging.FileHandler.count", "2"); 111 // using append mode 112 props.put("java.util.logging.FileHandler.append", "true"); 113 props 114 .put("java.util.logging.FileHandler.pattern", 115 "%t/log/java%u.test"); 116 } 117 118 /* 119 * @see TestCase#tearDown() 120 */ tearDown()121 protected void tearDown() throws Exception { 122 if (null != handler) { 123 handler.close(); 124 } 125 reset(TEMPPATH + SEP + "log", ""); 126 System.setErr(err); 127 super.tearDown(); 128 } 129 testConstructor_NoUsrHome()130 public void testConstructor_NoUsrHome() throws IOException { 131 System.clearProperty(USR_HOME_KEY); 132 133 try { 134 new FileHandler("%h/log_NoUsrHome.log"); 135 fail("should throw NullPointerException"); 136 } catch (NullPointerException e) { 137 // Expected 138 } finally { 139 if (HOMEPATH != null) { 140 System.setProperty(USR_HOME_KEY, HOMEPATH); 141 } 142 } 143 } 144 testConstructor_NoTmpDir()145 public void testConstructor_NoTmpDir() throws IOException { 146 System.clearProperty(TMP_DIR_KEY); 147 148 try { 149 new FileHandler("%t/log_NoTmpDir.log"); 150 } finally { 151 if (TEMPPATH != null) { 152 System.setProperty(TMP_DIR_KEY, TEMPPATH); 153 } 154 } 155 assertFalse(new File(TEMPPATH, "log_NoTmpDir.log").exists()); 156 assertTrue(new File(HOMEPATH, "log_NoTmpDir.log").exists()); 157 new File(HOMEPATH, "log_NoTmpDir.log").delete(); 158 } 159 testConstructor_NoTmpDir_NoUsrHome()160 public void testConstructor_NoTmpDir_NoUsrHome() throws IOException { 161 System.clearProperty(TMP_DIR_KEY); 162 System.clearProperty(USR_HOME_KEY); 163 164 try { 165 new FileHandler("%t/log_NoTmpDir_NoUsrHome.log"); 166 fail("should throw NullPointerException"); 167 } catch (NullPointerException e) { 168 // Expected 169 } finally { 170 if (TEMPPATH != null) { 171 System.setProperty(TMP_DIR_KEY, TEMPPATH); 172 } 173 if (HOMEPATH != null) { 174 System.setProperty(USR_HOME_KEY, HOMEPATH); 175 } 176 } 177 } 178 testLock()179 public void testLock() throws Exception { 180 FileOutputStream output = new FileOutputStream(TEMPPATH + SEP + "log" 181 + SEP + "java1.test.0"); 182 FileHandler h = new FileHandler(); 183 h.publish(r); 184 h.close(); 185 assertFileContent(TEMPPATH + SEP + "log", "java1.test.0", h 186 .getFormatter(), "UTF-8"); 187 output.close(); 188 } 189 190 /* 191 * Class under test for void FileHandler() 192 */ testFileHandler()193 public void testFileHandler() throws Exception { 194 assertEquals(handler.getEncoding(), "iso-8859-1"); 195 assertTrue(handler.getFilter() instanceof MockFilter); 196 assertTrue(handler.getFormatter() instanceof MockFormatter); 197 assertEquals(handler.getLevel(), Level.FINE); 198 assertNotNull(handler.getErrorManager()); 199 handler.publish(r); 200 handler.close(); 201 // output 3 times, and all records left 202 // append mode is true 203 for (int i = 0; i < 3; i++) { 204 handler = new FileHandler(); 205 handler.publish(r); 206 handler.close(); 207 } 208 assertFileContent(TEMPPATH + SEP + "log", "java0.test.0", 209 new LogRecord[] { r, null, r, null, r, null, r }, 210 new MockFormatter(), "UTF-8"); 211 } 212 testDefaultValue()213 public void testDefaultValue() throws Exception { 214 handler.publish(r); 215 handler.close(); 216 props.clear(); 217 manager.readConfiguration(EnvironmentHelper 218 .PropertiesToInputStream(props)); 219 handler = new FileHandler(); 220 assertNull(handler.getEncoding()); 221 assertNull(handler.getFilter()); 222 assertTrue(handler.getFormatter() instanceof XMLFormatter); 223 assertEquals(handler.getLevel(), Level.ALL); 224 assertNotNull(handler.getErrorManager()); 225 handler.publish(r); 226 handler.close(); 227 // output 3 times, and only one record left 228 // default append mode is false 229 for (int i = 0; i < 3; i++) { 230 handler = new FileHandler(); 231 handler.publish(r); 232 handler.close(); 233 } 234 assertFileContent(HOMEPATH, "java0.log", new XMLFormatter(), null); 235 } 236 assertFileContent(String homepath, String filename, Formatter formatter, String encoding)237 private void assertFileContent(String homepath, String filename, 238 Formatter formatter, String encoding) throws Exception { 239 assertFileContent(homepath, filename, new LogRecord[] { r }, formatter, encoding); 240 } 241 assertFileContent(String homepath, String filename, LogRecord[] lr, Formatter formatter, String encoding)242 private void assertFileContent(String homepath, String filename, 243 LogRecord[] lr, Formatter formatter, String encoding) throws Exception { 244 handler.close(); 245 String msg = ""; 246 // if formatter is null, the file content should be empty 247 // else the message should be formatted given records 248 if (null != formatter) { 249 StringBuffer sb = new StringBuffer(); 250 sb.append(formatter.getHead(handler)); 251 for (int i = 0; i < lr.length; i++) { 252 if (null == lr[i] && i < lr.length - 1) { 253 // if one record is null and is not the last record, means 254 // here is 255 // output completion point, should output tail, then output 256 // head 257 // (ready for next output) 258 sb.append(formatter.getTail(handler)); 259 sb.append(formatter.getHead(handler)); 260 } else { 261 sb.append(formatter.format(lr[i])); 262 } 263 } 264 sb.append(formatter.getTail(handler)); 265 msg = sb.toString(); 266 } 267 byte[] bytes = new byte[msg.length()]; 268 InputStream inputStream = null; 269 try { 270 inputStream = new BufferedInputStream(new FileInputStream(homepath 271 + SEP + filename)); 272 inputStream.read(bytes); 273 if (encoding == null) { 274 assertEquals(msg, new String(bytes)); 275 } else { 276 assertEquals(msg, new String(bytes, encoding)); 277 } 278 // assert has reached the end of the file 279 assertEquals(-1, inputStream.read()); 280 } finally { 281 try { 282 if (inputStream != null) { 283 inputStream.close(); 284 } 285 } catch (Exception e) { 286 // ignored 287 } 288 reset(homepath, filename); 289 } 290 } 291 292 /** 293 * Does a cleanup of given file 294 * 295 * @param homepath 296 * @param filename 297 */ reset(String homepath, String filename)298 private void reset(String homepath, String filename) { 299 File file = null; 300 try { 301 file = new File(homepath + SEP + filename); 302 if (file.isFile()) { 303 file.delete(); 304 } else if (file.isDirectory()) { 305 File[] files = file.listFiles(); 306 for (int i = 0; i < files.length; i++) { 307 files[i].delete(); 308 } 309 file.delete(); 310 } 311 } catch (Exception e) { 312 e.printStackTrace(); 313 } 314 try { 315 file = new File(homepath + SEP + filename + ".lck"); 316 file.delete(); 317 } catch (Exception e) { 318 e.printStackTrace(); 319 } 320 } 321 testLimitAndCount()322 public void testLimitAndCount() throws Exception { 323 handler.close(); 324 // very small limit value, count=2 325 // output, rename current output file to the second generation file 326 // close it and open a new file as rotation output 327 handler = new FileHandler("%t/testLimitCount%g", 1, 2, false); 328 handler.publish(r); 329 handler.close(); 330 assertFileContent(TEMPPATH, "testLimitCount1", handler.getFormatter(), "UTF-8"); 331 332 // very small limit value, count=1 333 // output once, rotate(equals to nothing output) 334 handler = new FileHandler("%t/testLimitCount%g", 1, 1, false); 335 handler.publish(r); 336 handler.close(); 337 assertFileContent(TEMPPATH, "testLimitCount0", new LogRecord[0], 338 handler.getFormatter(), "UTF-8"); 339 340 // normal case, limit is 60(>2*msg length <3*msg length), append is 341 // false 342 handler = new FileHandler("%t/testLimitCount%u", 60, 3, false); 343 LogRecord[] rs = new LogRecord[10]; 344 // batch output twice to test the append mode 345 for (int i = 0; i < 5; i++) { 346 rs[i] = new LogRecord(Level.SEVERE, "msg" + i); 347 handler.publish(rs[i]); 348 } 349 handler.close(); 350 handler = new FileHandler("%t/testLimitCount%u", 60, 3, false); 351 for (int i = 5; i < 10; i++) { 352 rs[i] = new LogRecord(Level.SEVERE, "msg" + i); 353 handler.publish(rs[i]); 354 } 355 356 assertFileContent(TEMPPATH, "testLimitCount0.1", new LogRecord[] { 357 rs[5], rs[6], rs[7] }, handler.getFormatter(), "UTF-8"); 358 assertFileContent(TEMPPATH, "testLimitCount0.0", new LogRecord[] { 359 rs[8], rs[9] }, handler.getFormatter(), "UTF-8"); 360 361 // normal case, limit is 60(>2*msg length <3*msg length), append is true 362 handler = new FileHandler("%t/testLimitCount%u", 60, 3, false); 363 // batch output twice to test the append mode 364 for (int i = 0; i < 5; i++) { 365 rs[i] = new LogRecord(Level.SEVERE, "msg" + i); 366 handler.publish(rs[i]); 367 } 368 handler.close(); 369 handler = new FileHandler("%t/testLimitCount%u", 60, 3, true); 370 for (int i = 5; i < 10; i++) { 371 rs[i] = new LogRecord(Level.SEVERE, "msg" + i); 372 handler.publish(rs[i]); 373 } 374 handler.close(); 375 assertFileContent(TEMPPATH, "testLimitCount0.2", new LogRecord[] { 376 rs[3], rs[4], null, rs[5] }, handler.getFormatter(), "UTF-8"); 377 assertFileContent(TEMPPATH, "testLimitCount0.1", new LogRecord[] { 378 rs[6], rs[7], rs[8] }, handler.getFormatter(), "UTF-8"); 379 assertFileContent(TEMPPATH, "testLimitCount0.0", 380 new LogRecord[] { rs[9] }, handler.getFormatter(), "UTF-8"); 381 382 FileHandler h1 = null; 383 FileHandler h2 = null; 384 try { 385 File logDir = new File("log"); 386 reset("log", ""); 387 logDir.mkdir(); 388 h1 = new FileHandler("log/a", 0, 1); 389 assertNotNull(h1); 390 h2 = new FileHandler("log/a", 0, 1, false); 391 assertNotNull(h2); 392 } finally { 393 try { 394 h1.close(); 395 } catch (Exception e) { 396 } 397 try { 398 h2.close(); 399 } catch (Exception e) { 400 } 401 reset("log", ""); 402 } 403 } 404 testInvalidProperty()405 public void testInvalidProperty() throws Exception { 406 props.put("java.util.logging.FileHandler.level", "null"); 407 props.put("java.util.logging.FileHandler.filter", className 408 + "$MockFilte"); 409 props.put("java.util.logging.FileHandler.formatter", className 410 + "$MockFormatte"); 411 props.put("java.util.logging.FileHandler.encoding", "ut"); 412 // limit to only two message 413 props.put("java.util.logging.FileHandler.limit", "-1"); 414 // rotation count is 2 415 props.put("java.util.logging.FileHandler.count", "-1"); 416 // using append mode 417 props.put("java.util.logging.FileHandler.append", "bad"); 418 419 handler.close(); 420 421 manager.readConfiguration(EnvironmentHelper 422 .PropertiesToInputStream(props)); 423 handler = new FileHandler(); 424 assertEquals(Level.ALL, handler.getLevel()); 425 assertNull(handler.getFilter()); 426 assertTrue(handler.getFormatter() instanceof XMLFormatter); 427 assertNull(handler.getEncoding()); 428 handler.close(); 429 430 props.put("java.util.logging.FileHandler.pattern", ""); 431 manager.readConfiguration(EnvironmentHelper 432 .PropertiesToInputStream(props)); 433 try { 434 handler = new FileHandler(); 435 fail("shouldn't open file with empty name"); 436 } catch (NullPointerException e) { 437 } 438 } 439 testInvalidParams()440 public void testInvalidParams() throws IOException { 441 442 // %t and %p parsing can add file separator automatically 443 FileHandler h1 = new FileHandler("%taaa"); 444 h1.close(); 445 File file = new File(TEMPPATH + SEP + "aaa"); 446 assertTrue(file.exists()); 447 reset(TEMPPATH, "aaa"); 448 449 // always parse special pattern 450 try { 451 h1 = new FileHandler("%t/%h"); 452 fail("should throw null exception"); 453 } catch (FileNotFoundException e) { 454 } 455 h1 = new FileHandler("%t%g"); 456 h1.close(); 457 file = new File(TEMPPATH + SEP + "0"); 458 assertTrue(file.exists()); 459 reset(TEMPPATH, "0"); 460 h1 = new FileHandler("%t%u%g"); 461 h1.close(); 462 file = new File(TEMPPATH + SEP + "00"); 463 assertTrue(file.exists()); 464 reset(TEMPPATH, "00"); 465 466 // this is normal case 467 h1 = new FileHandler("%t/%u%g%%g"); 468 h1.close(); 469 file = new File(TEMPPATH + SEP + "00%g"); 470 assertTrue(file.exists()); 471 reset(TEMPPATH, "00%g"); 472 473 // multi separator has no effect 474 h1 = new FileHandler("//%t//multi%g"); 475 h1.close(); 476 file = new File(TEMPPATH + SEP + "multi0"); 477 assertTrue(file.exists()); 478 reset(TEMPPATH, "multi0"); 479 480 // bad directory, IOException 481 try { 482 h1 = new FileHandler("%t/baddir/multi%g"); 483 fail("should throw IO exception"); 484 } catch (IOException e) { 485 } 486 file = new File(TEMPPATH + SEP + "baddir" + SEP + "multi0"); 487 assertFalse(file.exists()); 488 489 try { 490 new FileHandler(null); 491 fail("should throw null exception"); 492 } catch (NullPointerException e) { 493 } 494 try { 495 handler.publish(null); 496 } catch (NullPointerException e) { 497 fail("should not throw NPE"); 498 } 499 try { 500 new FileHandler(null, false); 501 fail("should throw null exception"); 502 } catch (NullPointerException e) { 503 } 504 try { 505 // regression test for Harmony-1299 506 new FileHandler(""); 507 fail("should throw IllegalArgumentException"); 508 } catch (IllegalArgumentException e) { 509 // expected 510 } 511 try { 512 new FileHandler("%t/java%u", 0, 0); 513 fail("should throw IllegalArgumentException"); 514 } catch (IllegalArgumentException e) { 515 } 516 try { 517 new FileHandler("%t/java%u", -1, 1); 518 fail("should throw IllegalArgumentException"); 519 } catch (IllegalArgumentException e) { 520 } 521 } 522 523 // set output stream still works, just like super StreamHandler testSetOutputStream()524 public void testSetOutputStream() throws Exception { 525 MockFileHandler handler = new MockFileHandler("%h/setoutput.log"); 526 handler.setFormatter(new MockFormatter()); 527 handler.publish(r); 528 529 ByteArrayOutputStream out = new ByteArrayOutputStream(); 530 handler.publicSetOutputStream(out); 531 handler.publish(r); 532 handler.close(); 533 String msg = new String(out.toByteArray()); 534 Formatter f = handler.getFormatter(); 535 assertEquals(msg, f.getHead(handler) + f.format(r) + f.getTail(handler)); 536 assertFileContent(HOMEPATH, "setoutput.log", handler.getFormatter(), null); 537 } 538 539 /* 540 * Class under test for void FileHandler(String) 541 */ testFileHandlerString()542 public void testFileHandlerString() throws Exception { 543 // test if unique ids not specified, it will append at the end 544 // no generation number is used 545 FileHandler h = new FileHandler("%t/log/string"); 546 FileHandler h2 = new FileHandler("%t/log/string"); 547 FileHandler h3 = new FileHandler("%t/log/string"); 548 FileHandler h4 = new FileHandler("%t/log/string"); 549 h.publish(r); 550 h2.publish(r); 551 h3.publish(r); 552 h4.publish(r); 553 h.close(); 554 h2.close(); 555 h3.close(); 556 h4.close(); 557 assertFileContent(TEMPPATH + SEP + "log", "string", h.getFormatter(), "UTF-8"); 558 assertFileContent(TEMPPATH + SEP + "log", "string.1", h.getFormatter(), "UTF-8"); 559 assertFileContent(TEMPPATH + SEP + "log", "string.2", h.getFormatter(), "UTF-8"); 560 assertFileContent(TEMPPATH + SEP + "log", "string.3", h.getFormatter(), "UTF-8"); 561 562 // default is append mode 563 FileHandler h6 = new FileHandler("%t/log/string%u.log"); 564 h6.publish(r); 565 h6.close(); 566 FileHandler h7 = new FileHandler("%t/log/string%u.log"); 567 h7.publish(r); 568 h7.close(); 569 try { 570 assertFileContent(TEMPPATH + SEP + "log", "string0.log", h 571 .getFormatter(), "UTF-8"); 572 fail("should assertion failed"); 573 } catch (Error e) { 574 } 575 File file = new File(TEMPPATH + SEP + "log"); 576 assertTrue(file.list().length <= 2); 577 578 // test unique ids 579 FileHandler h8 = new FileHandler("%t/log/%ustring%u.log"); 580 h8.publish(r); 581 FileHandler h9 = new FileHandler("%t/log/%ustring%u.log"); 582 h9.publish(r); 583 h9.close(); 584 h8.close(); 585 assertFileContent(TEMPPATH + SEP + "log", "0string0.log", h 586 .getFormatter(), "UTF-8"); 587 assertFileContent(TEMPPATH + SEP + "log", "1string1.log", h 588 .getFormatter(), "UTF-8"); 589 file = new File(TEMPPATH + SEP + "log"); 590 assertTrue(file.list().length <= 2); 591 } 592 testEmptyPattern_3params()593 public void testEmptyPattern_3params() throws SecurityException, 594 IOException { 595 // regression HARMONY-2421 596 try { 597 new FileHandler(new String(), 1, 1); 598 fail("Expected an IllegalArgumentException"); 599 } catch (IllegalArgumentException e) { 600 // Expected 601 } 602 } 603 testEmptyPattern_2params()604 public void testEmptyPattern_2params() throws SecurityException, 605 IOException { 606 // regression HARMONY-2421 607 try { 608 new FileHandler(new String(), true); 609 fail("Expected an IllegalArgumentException"); 610 } catch (IllegalArgumentException e) { 611 // Expected 612 } 613 } 614 testEmptyPattern_4params()615 public void testEmptyPattern_4params() throws SecurityException, 616 IOException { 617 // regression HARMONY-2421 618 try { 619 new FileHandler(new String(), 1, 1, true); 620 fail("Expected an IllegalArgumentException"); 621 } catch (IllegalArgumentException e) { 622 // Expected 623 } 624 } 625 626 /* 627 * mock classes 628 */ 629 public static class MockFilter implements Filter { isLoggable(LogRecord record)630 public boolean isLoggable(LogRecord record) { 631 return !record.getMessage().equals("false"); 632 } 633 } 634 635 public static class MockFormatter extends Formatter { format(LogRecord r)636 public String format(LogRecord r) { 637 if (null == r) { 638 return ""; 639 } 640 return r.getMessage() + " by MockFormatter\n"; 641 } 642 getTail(Handler h)643 public String getTail(Handler h) { 644 return "tail\n"; 645 } 646 getHead(Handler h)647 public String getHead(Handler h) { 648 return "head\n"; 649 } 650 } 651 652 public static class MockFileHandler extends FileHandler { MockFileHandler()653 public MockFileHandler() throws IOException { 654 super(); 655 } 656 MockFileHandler(String pattern)657 public MockFileHandler(String pattern) throws IOException { 658 super(pattern); 659 } 660 publicSetOutputStream(OutputStream stream)661 public void publicSetOutputStream(OutputStream stream) { 662 super.setOutputStream(stream); 663 } 664 } 665 } 666