View Javadoc
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    *      https://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.commons.io.input;
19  
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  
22  import java.nio.file.Files;
23  import java.nio.file.Path;
24  import java.util.concurrent.CompletableFuture;
25  import java.util.concurrent.ExecutorService;
26  import java.util.concurrent.Executors;
27  import java.util.concurrent.TimeUnit;
28  
29  import org.junit.jupiter.api.AfterEach;
30  import org.junit.jupiter.api.Test;
31  import org.junit.jupiter.api.condition.DisabledOnOs;
32  import org.junit.jupiter.api.condition.OS;
33  
34  /**
35   * Tests {@link Tailer} for <a href=https://issues.apache.org/jira/browse/IO-889">IO-889</a>.
36   */
37  public class TailerCloseTest {
38  
39      private class TailerTestListener extends TailerListenerAdapter {
40  
41          @Override
42          public void handle(final Exception ex) {
43              super.handle(ex);
44              result.completeExceptionally(ex);
45          }
46  
47          @Override
48          public void handle(final String line) {
49              super.handle(line);
50              result.complete(line);
51          }
52      }
53  
54      private static Thread newDaemonThread(final Runnable runnable) {
55          final Thread thread = new Thread(runnable, "commons-io-tailer");
56          thread.setDaemon(true);
57          return thread;
58      }
59  
60      private ExecutorService executorService;
61      private Path path;
62      private final CompletableFuture<String> result = new CompletableFuture<>();
63  
64      @AfterEach
65      public void tearDown() throws Exception {
66          // wait for the tailer task and delete log file if previous delete failed
67          if (executorService != null) {
68              executorService.shutdown();
69              executorService.awaitTermination(60, TimeUnit.SECONDS);
70          }
71          if (path != null && Files.exists(path)) {
72              Files.delete(path);
73          }
74      }
75  
76      @DisabledOnOs(value = OS.WINDOWS)
77      @Test
78      public void testCloseTailer() throws Exception {
79          path = Files.createTempFile("TailerTestFile", ".log");
80          // same as default Tailer executor
81          executorService = Executors.newSingleThreadExecutor(TailerCloseTest::newDaemonThread);
82          try (
83          // @formatter:off
84              Tailer tailer = Tailer
85                      .builder()
86                      .setExecutorService(executorService)
87                      .setTailerListener(new TailerTestListener())
88                      .setFile(path.toFile())
89                      .get()) {
90              // @formatter:on
91              Files.write(path, "aaa\n".getBytes());
92              // wait for the background thread to open the file
93              assertEquals("aaa", result.get(60, TimeUnit.SECONDS));
94          }
95          // delete file after closing Tailer
96          Files.delete(path);
97      }
98  }