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+ }
0 commit comments