Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.logging/share/classes/java/util/logging/FileHandler.java
41159 views
1
/*
2
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package java.util.logging;
27
28
import static java.nio.file.StandardOpenOption.APPEND;
29
import static java.nio.file.StandardOpenOption.CREATE_NEW;
30
import static java.nio.file.StandardOpenOption.WRITE;
31
32
import java.io.BufferedOutputStream;
33
import java.io.File;
34
import java.io.FileOutputStream;
35
import java.io.IOException;
36
import java.io.OutputStream;
37
import java.nio.channels.FileChannel;
38
import java.nio.channels.OverlappingFileLockException;
39
import java.nio.file.AccessDeniedException;
40
import java.nio.file.FileAlreadyExistsException;
41
import java.nio.file.Files;
42
import java.nio.file.LinkOption;
43
import java.nio.file.NoSuchFileException;
44
import java.nio.file.Path;
45
import java.nio.file.Paths;
46
import java.security.AccessController;
47
import java.security.PrivilegedAction;
48
import java.util.HashSet;
49
import java.util.Set;
50
51
/**
52
* Simple file logging {@code Handler}.
53
* <p>
54
* The {@code FileHandler} can either write to a specified file,
55
* or it can write to a rotating set of files.
56
* <p>
57
* For a rotating set of files, as each file reaches a given size
58
* limit, it is closed, rotated out, and a new file opened.
59
* Successively older files are named by adding "0", "1", "2",
60
* etc. into the base filename.
61
* <p>
62
* By default buffering is enabled in the IO libraries but each log
63
* record is flushed out when it is complete.
64
* <p>
65
* By default the {@code XMLFormatter} class is used for formatting.
66
* <p>
67
* <b>Configuration:</b>
68
* By default each {@code FileHandler} is initialized using the following
69
* {@code LogManager} configuration properties where {@code <handler-name>}
70
* refers to the fully-qualified class name of the handler.
71
* If properties are not defined
72
* (or have invalid values) then the specified default values are used.
73
* <ul>
74
* <li> &lt;handler-name&gt;.level
75
* specifies the default level for the {@code Handler}
76
* (defaults to {@code Level.ALL}). </li>
77
* <li> &lt;handler-name&gt;.filter
78
* specifies the name of a {@code Filter} class to use
79
* (defaults to no {@code Filter}). </li>
80
* <li> &lt;handler-name&gt;.formatter
81
* specifies the name of a {@code Formatter} class to use
82
* (defaults to {@code java.util.logging.XMLFormatter}) </li>
83
* <li> &lt;handler-name&gt;.encoding
84
* the name of the character set encoding to use (defaults to
85
* the default platform encoding). </li>
86
* <li> &lt;handler-name&gt;.limit
87
* specifies an approximate maximum amount to write (in bytes)
88
* to any one file. If this is zero, then there is no limit.
89
* (Defaults to no limit). </li>
90
* <li> &lt;handler-name&gt;.count
91
* specifies how many output files to cycle through (defaults to 1). </li>
92
* <li> &lt;handler-name&gt;.pattern
93
* specifies a pattern for generating the output file name. See
94
* below for details. (Defaults to "%h/java%u.log"). </li>
95
* <li> &lt;handler-name&gt;.append
96
* specifies whether the FileHandler should append onto
97
* any existing files (defaults to false). </li>
98
* <li> &lt;handler-name&gt;.maxLocks
99
* specifies the maximum number of concurrent locks held by
100
* FileHandler (defaults to 100). </li>
101
* </ul>
102
* <p>
103
* For example, the properties for {@code FileHandler} would be:
104
* <ul>
105
* <li> java.util.logging.FileHandler.level=INFO </li>
106
* <li> java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter </li>
107
* </ul>
108
* <p>
109
* For a custom handler, e.g. com.foo.MyHandler, the properties would be:
110
* <ul>
111
* <li> com.foo.MyHandler.level=INFO </li>
112
* <li> com.foo.MyHandler.formatter=java.util.logging.SimpleFormatter </li>
113
* </ul>
114
* <p>
115
* A pattern consists of a string that includes the following special
116
* components that will be replaced at runtime:
117
* <ul>
118
* <li> "/" the local pathname separator </li>
119
* <li> "%t" the system temporary directory </li>
120
* <li> "%h" the value of the "user.home" system property </li>
121
* <li> "%g" the generation number to distinguish rotated logs </li>
122
* <li> "%u" a unique number to resolve conflicts </li>
123
* <li> "%%" translates to a single percent sign "%" </li>
124
* </ul>
125
* If no "%g" field has been specified and the file count is greater
126
* than one, then the generation number will be added to the end of
127
* the generated filename, after a dot.
128
* <p>
129
* Thus for example a pattern of "%t/java%g.log" with a count of 2
130
* would typically cause log files to be written on Solaris to
131
* /var/tmp/java0.log and /var/tmp/java1.log whereas on Windows 95 they
132
* would be typically written to C:\TEMP\java0.log and C:\TEMP\java1.log
133
* <p>
134
* Generation numbers follow the sequence 0, 1, 2, etc.
135
* <p>
136
* Normally the "%u" unique field is set to 0. However, if the {@code FileHandler}
137
* tries to open the filename and finds the file is currently in use by
138
* another process it will increment the unique number field and try
139
* again. This will be repeated until {@code FileHandler} finds a file name that
140
* is not currently in use. If there is a conflict and no "%u" field has
141
* been specified, it will be added at the end of the filename after a dot.
142
* (This will be after any automatically added generation number.)
143
* <p>
144
* Thus if three processes were all trying to log to fred%u.%g.txt then
145
* they might end up using fred0.0.txt, fred1.0.txt, fred2.0.txt as
146
* the first file in their rotating sequences.
147
* <p>
148
* Note that the use of unique ids to avoid conflicts is only guaranteed
149
* to work reliably when using a local disk file system.
150
*
151
* @since 1.4
152
*/
153
154
public class FileHandler extends StreamHandler {
155
private MeteredStream meter;
156
private boolean append;
157
private long limit; // zero => no limit.
158
private int count;
159
private String pattern;
160
private String lockFileName;
161
private FileChannel lockFileChannel;
162
private File files[];
163
private static final int MAX_LOCKS = 100;
164
private int maxLocks = MAX_LOCKS;
165
private static final Set<String> locks = new HashSet<>();
166
167
/**
168
* A metered stream is a subclass of OutputStream that
169
* (a) forwards all its output to a target stream
170
* (b) keeps track of how many bytes have been written
171
*/
172
private static final class MeteredStream extends OutputStream {
173
final OutputStream out;
174
long written;
175
176
MeteredStream(OutputStream out, long written) {
177
this.out = out;
178
this.written = written;
179
}
180
181
@Override
182
public void write(int b) throws IOException {
183
out.write(b);
184
written++;
185
}
186
187
@Override
188
public void write(byte buff[]) throws IOException {
189
out.write(buff);
190
written += buff.length;
191
}
192
193
@Override
194
public void write(byte buff[], int off, int len) throws IOException {
195
out.write(buff,off,len);
196
written += len;
197
}
198
199
@Override
200
public void flush() throws IOException {
201
out.flush();
202
}
203
204
@Override
205
public void close() throws IOException {
206
out.close();
207
}
208
}
209
210
private void open(File fname, boolean append) throws IOException {
211
long len = 0;
212
if (append) {
213
len = fname.length();
214
}
215
FileOutputStream fout = new FileOutputStream(fname.toString(), append);
216
BufferedOutputStream bout = new BufferedOutputStream(fout);
217
meter = new MeteredStream(bout, len);
218
setOutputStream(meter);
219
}
220
221
/**
222
* Configure a FileHandler from LogManager properties and/or default values
223
* as specified in the class javadoc.
224
*/
225
private void configure() {
226
LogManager manager = LogManager.getLogManager();
227
228
String cname = getClass().getName();
229
230
pattern = manager.getStringProperty(cname + ".pattern", "%h/java%u.log");
231
limit = manager.getLongProperty(cname + ".limit", 0);
232
if (limit < 0) {
233
limit = 0;
234
}
235
count = manager.getIntProperty(cname + ".count", 1);
236
if (count <= 0) {
237
count = 1;
238
}
239
append = manager.getBooleanProperty(cname + ".append", false);
240
setLevel(manager.getLevelProperty(cname + ".level", Level.ALL));
241
setFilter(manager.getFilterProperty(cname + ".filter", null));
242
setFormatter(manager.getFormatterProperty(cname + ".formatter", new XMLFormatter()));
243
// Initialize maxLocks from the logging.properties file.
244
// If invalid/no property is provided 100 will be used as a default value.
245
maxLocks = manager.getIntProperty(cname + ".maxLocks", MAX_LOCKS);
246
if(maxLocks <= 0) {
247
maxLocks = MAX_LOCKS;
248
}
249
try {
250
setEncoding(manager.getStringProperty(cname +".encoding", null));
251
} catch (Exception ex) {
252
try {
253
setEncoding(null);
254
} catch (Exception ex2) {
255
// doing a setEncoding with null should always work.
256
// assert false;
257
}
258
}
259
}
260
261
262
/**
263
* Construct a default {@code FileHandler}. This will be configured
264
* entirely from {@code LogManager} properties (or their default values).
265
*
266
* @throws IOException if there are IO problems opening the files.
267
* @throws SecurityException if a security manager exists and if
268
* the caller does not have {@code LoggingPermission("control"))}.
269
* @throws NullPointerException if pattern property is an empty String.
270
*/
271
public FileHandler() throws IOException, SecurityException {
272
checkPermission();
273
configure();
274
// pattern will have been set by configure. check that it's not
275
// empty.
276
if (pattern.isEmpty()) {
277
throw new NullPointerException();
278
}
279
openFiles();
280
}
281
282
/**
283
* Initialize a {@code FileHandler} to write to the given filename.
284
* <p>
285
* The {@code FileHandler} is configured based on {@code LogManager}
286
* properties (or their default values) except that the given pattern
287
* argument is used as the filename pattern, the file limit is
288
* set to no limit, and the file count is set to one.
289
* <p>
290
* There is no limit on the amount of data that may be written,
291
* so use this with care.
292
*
293
* @param pattern the name of the output file
294
* @throws IOException if there are IO problems opening the files.
295
* @throws SecurityException if a security manager exists and if
296
* the caller does not have {@code LoggingPermission("control")}.
297
* @throws IllegalArgumentException if pattern is an empty string
298
*/
299
public FileHandler(String pattern) throws IOException, SecurityException {
300
if (pattern.length() < 1 ) {
301
throw new IllegalArgumentException();
302
}
303
checkPermission();
304
configure();
305
this.pattern = pattern;
306
this.limit = 0;
307
this.count = 1;
308
openFiles();
309
}
310
311
/**
312
* Initialize a {@code FileHandler} to write to the given filename,
313
* with optional append.
314
* <p>
315
* The {@code FileHandler} is configured based on {@code LogManager}
316
* properties (or their default values) except that the given pattern
317
* argument is used as the filename pattern, the file limit is
318
* set to no limit, the file count is set to one, and the append
319
* mode is set to the given {@code append} argument.
320
* <p>
321
* There is no limit on the amount of data that may be written,
322
* so use this with care.
323
*
324
* @param pattern the name of the output file
325
* @param append specifies append mode
326
* @throws IOException if there are IO problems opening the files.
327
* @throws SecurityException if a security manager exists and if
328
* the caller does not have {@code LoggingPermission("control")}.
329
* @throws IllegalArgumentException if pattern is an empty string
330
*/
331
public FileHandler(String pattern, boolean append) throws IOException,
332
SecurityException {
333
if (pattern.length() < 1 ) {
334
throw new IllegalArgumentException();
335
}
336
checkPermission();
337
configure();
338
this.pattern = pattern;
339
this.limit = 0;
340
this.count = 1;
341
this.append = append;
342
openFiles();
343
}
344
345
/**
346
* Initialize a {@code FileHandler} to write to a set of files. When
347
* (approximately) the given limit has been written to one file,
348
* another file will be opened. The output will cycle through a set
349
* of count files.
350
* <p>
351
* The {@code FileHandler} is configured based on {@code LogManager}
352
* properties (or their default values) except that the given pattern
353
* argument is used as the filename pattern, the file limit is
354
* set to the limit argument, and the file count is set to the
355
* given count argument.
356
* <p>
357
* The count must be at least 1.
358
*
359
* @param pattern the pattern for naming the output file
360
* @param limit the maximum number of bytes to write to any one file
361
* @param count the number of files to use
362
* @throws IOException if there are IO problems opening the files.
363
* @throws SecurityException if a security manager exists and if
364
* the caller does not have {@code LoggingPermission("control")}.
365
* @throws IllegalArgumentException if {@code limit < 0}, or {@code count < 1}.
366
* @throws IllegalArgumentException if pattern is an empty string
367
*/
368
public FileHandler(String pattern, int limit, int count)
369
throws IOException, SecurityException {
370
if (limit < 0 || count < 1 || pattern.length() < 1) {
371
throw new IllegalArgumentException();
372
}
373
checkPermission();
374
configure();
375
this.pattern = pattern;
376
this.limit = limit;
377
this.count = count;
378
openFiles();
379
}
380
381
/**
382
* Initialize a {@code FileHandler} to write to a set of files
383
* with optional append. When (approximately) the given limit has
384
* been written to one file, another file will be opened. The
385
* output will cycle through a set of count files.
386
* <p>
387
* The {@code FileHandler} is configured based on {@code LogManager}
388
* properties (or their default values) except that the given pattern
389
* argument is used as the filename pattern, the file limit is
390
* set to the limit argument, and the file count is set to the
391
* given count argument, and the append mode is set to the given
392
* {@code append} argument.
393
* <p>
394
* The count must be at least 1.
395
*
396
* @param pattern the pattern for naming the output file
397
* @param limit the maximum number of bytes to write to any one file
398
* @param count the number of files to use
399
* @param append specifies append mode
400
* @throws IOException if there are IO problems opening the files.
401
* @throws SecurityException if a security manager exists and if
402
* the caller does not have {@code LoggingPermission("control")}.
403
* @throws IllegalArgumentException if {@code limit < 0}, or {@code count < 1}.
404
* @throws IllegalArgumentException if pattern is an empty string
405
*
406
*/
407
public FileHandler(String pattern, int limit, int count, boolean append)
408
throws IOException, SecurityException {
409
this(pattern, (long)limit, count, append);
410
}
411
412
/**
413
* Initialize a {@code FileHandler} to write to a set of files
414
* with optional append. When (approximately) the given limit has
415
* been written to one file, another file will be opened. The
416
* output will cycle through a set of count files.
417
* <p>
418
* The {@code FileHandler} is configured based on {@code LogManager}
419
* properties (or their default values) except that the given pattern
420
* argument is used as the filename pattern, the file limit is
421
* set to the limit argument, and the file count is set to the
422
* given count argument, and the append mode is set to the given
423
* {@code append} argument.
424
* <p>
425
* The count must be at least 1.
426
*
427
* @param pattern the pattern for naming the output file
428
* @param limit the maximum number of bytes to write to any one file
429
* @param count the number of files to use
430
* @param append specifies append mode
431
* @throws IOException if there are IO problems opening the files.
432
* @throws SecurityException if a security manager exists and if
433
* the caller does not have {@code LoggingPermission("control")}.
434
* @throws IllegalArgumentException if {@code limit < 0}, or {@code count < 1}.
435
* @throws IllegalArgumentException if pattern is an empty string
436
*
437
* @since 9
438
*
439
*/
440
public FileHandler(String pattern, long limit, int count, boolean append)
441
throws IOException {
442
if (limit < 0 || count < 1 || pattern.length() < 1) {
443
throw new IllegalArgumentException();
444
}
445
checkPermission();
446
configure();
447
this.pattern = pattern;
448
this.limit = limit;
449
this.count = count;
450
this.append = append;
451
openFiles();
452
}
453
454
private boolean isParentWritable(Path path) {
455
Path parent = path.getParent();
456
if (parent == null) {
457
parent = path.toAbsolutePath().getParent();
458
}
459
return parent != null && Files.isWritable(parent);
460
}
461
462
/**
463
* Open the set of output files, based on the configured
464
* instance variables.
465
*/
466
private void openFiles() throws IOException {
467
LogManager manager = LogManager.getLogManager();
468
manager.checkPermission();
469
if (count < 1) {
470
throw new IllegalArgumentException("file count = " + count);
471
}
472
if (limit < 0) {
473
limit = 0;
474
}
475
476
// All constructors check that pattern is neither null nor empty.
477
assert pattern != null : "pattern should not be null";
478
assert !pattern.isEmpty() : "pattern should not be empty";
479
480
// We register our own ErrorManager during initialization
481
// so we can record exceptions.
482
InitializationErrorManager em = new InitializationErrorManager();
483
setErrorManager(em);
484
485
// Create a lock file. This grants us exclusive access
486
// to our set of output files, as long as we are alive.
487
int unique = -1;
488
for (;;) {
489
unique++;
490
if (unique > maxLocks) {
491
throw new IOException("Couldn't get lock for " + pattern);
492
}
493
// Generate a lock file name from the "unique" int.
494
lockFileName = generate(pattern, 0, unique).toString() + ".lck";
495
// Now try to lock that filename.
496
// Because some systems (e.g., Solaris) can only do file locks
497
// between processes (and not within a process), we first check
498
// if we ourself already have the file locked.
499
synchronized(locks) {
500
if (locks.contains(lockFileName)) {
501
// We already own this lock, for a different FileHandler
502
// object. Try again.
503
continue;
504
}
505
506
final Path lockFilePath = Paths.get(lockFileName);
507
FileChannel channel = null;
508
int retries = -1;
509
boolean fileCreated = false;
510
while (channel == null && retries++ < 1) {
511
try {
512
channel = FileChannel.open(lockFilePath,
513
CREATE_NEW, WRITE);
514
fileCreated = true;
515
} catch (AccessDeniedException ade) {
516
// This can be either a temporary, or a more permanent issue.
517
// The lock file might be still pending deletion from a previous run
518
// (temporary), or the parent directory might not be accessible,
519
// not writable, etc..
520
// If we can write to the current directory, and this is a regular file,
521
// let's try again.
522
if (Files.isRegularFile(lockFilePath, LinkOption.NOFOLLOW_LINKS)
523
&& isParentWritable(lockFilePath)) {
524
// Try again. If it doesn't work, then this will
525
// eventually ensure that we increment "unique" and
526
// use another file name.
527
continue;
528
} else {
529
throw ade; // no need to retry
530
}
531
} catch (FileAlreadyExistsException ix) {
532
// This may be a zombie file left over by a previous
533
// execution. Reuse it - but only if we can actually
534
// write to its directory.
535
// Note that this is a situation that may happen,
536
// but not too frequently.
537
if (Files.isRegularFile(lockFilePath, LinkOption.NOFOLLOW_LINKS)
538
&& isParentWritable(lockFilePath)) {
539
try {
540
channel = FileChannel.open(lockFilePath,
541
WRITE, APPEND);
542
} catch (NoSuchFileException x) {
543
// Race condition - retry once, and if that
544
// fails again just try the next name in
545
// the sequence.
546
continue;
547
} catch(IOException x) {
548
// the file may not be writable for us.
549
// try the next name in the sequence
550
break;
551
}
552
} else {
553
// at this point channel should still be null.
554
// break and try the next name in the sequence.
555
break;
556
}
557
}
558
}
559
560
if (channel == null) continue; // try the next name;
561
lockFileChannel = channel;
562
563
boolean available;
564
try {
565
available = lockFileChannel.tryLock() != null;
566
// We got the lock OK.
567
// At this point we could call File.deleteOnExit().
568
// However, this could have undesirable side effects
569
// as indicated by JDK-4872014. So we will instead
570
// rely on the fact that close() will remove the lock
571
// file and that whoever is creating FileHandlers should
572
// be responsible for closing them.
573
} catch (IOException ix) {
574
// We got an IOException while trying to get the lock.
575
// This normally indicates that locking is not supported
576
// on the target directory. We have to proceed without
577
// getting a lock. Drop through, but only if we did
578
// create the file...
579
available = fileCreated;
580
} catch (OverlappingFileLockException x) {
581
// someone already locked this file in this VM, through
582
// some other channel - that is - using something else
583
// than new FileHandler(...);
584
// continue searching for an available lock.
585
available = false;
586
}
587
if (available) {
588
// We got the lock. Remember it.
589
locks.add(lockFileName);
590
break;
591
}
592
593
// We failed to get the lock. Try next file.
594
lockFileChannel.close();
595
}
596
}
597
598
files = new File[count];
599
for (int i = 0; i < count; i++) {
600
files[i] = generate(pattern, i, unique);
601
}
602
603
// Create the initial log file.
604
if (append) {
605
open(files[0], true);
606
} else {
607
rotate();
608
}
609
610
// Did we detect any exceptions during initialization?
611
Exception ex = em.lastException;
612
if (ex != null) {
613
if (ex instanceof IOException) {
614
throw (IOException) ex;
615
} else if (ex instanceof SecurityException) {
616
throw (SecurityException) ex;
617
} else {
618
throw new IOException("Exception: " + ex);
619
}
620
}
621
622
// Install the normal default ErrorManager.
623
setErrorManager(new ErrorManager());
624
}
625
626
/**
627
* Generate a file based on a user-supplied pattern, generation number,
628
* and an integer uniqueness suffix
629
* @param pattern the pattern for naming the output file
630
* @param generation the generation number to distinguish rotated logs
631
* @param unique a unique number to resolve conflicts
632
* @return the generated File
633
* @throws IOException
634
*/
635
private File generate(String pattern, int generation, int unique)
636
throws IOException
637
{
638
return generate(pattern, count, generation, unique);
639
}
640
641
// The static method here is provided for whitebox testing of the algorithm.
642
static File generate(String pat, int count, int generation, int unique)
643
throws IOException
644
{
645
Path path = Paths.get(pat);
646
Path result = null;
647
boolean sawg = false;
648
boolean sawu = false;
649
StringBuilder word = new StringBuilder();
650
Path prev = null;
651
for (Path elem : path) {
652
if (prev != null) {
653
prev = prev.resolveSibling(word.toString());
654
result = result == null ? prev : result.resolve(prev);
655
}
656
String pattern = elem.toString();
657
int ix = 0;
658
word.setLength(0);
659
while (ix < pattern.length()) {
660
char ch = pattern.charAt(ix);
661
ix++;
662
char ch2 = 0;
663
if (ix < pattern.length()) {
664
ch2 = Character.toLowerCase(pattern.charAt(ix));
665
}
666
if (ch == '%') {
667
if (ch2 == 't') {
668
String tmpDir = System.getProperty("java.io.tmpdir");
669
if (tmpDir == null) {
670
tmpDir = System.getProperty("user.home");
671
}
672
result = Paths.get(tmpDir);
673
ix++;
674
word.setLength(0);
675
continue;
676
} else if (ch2 == 'h') {
677
result = Paths.get(System.getProperty("user.home"));
678
if (jdk.internal.misc.VM.isSetUID()) {
679
// Ok, we are in a set UID program. For safety's sake
680
// we disallow attempts to open files relative to %h.
681
throw new IOException("can't use %h in set UID program");
682
}
683
ix++;
684
word.setLength(0);
685
continue;
686
} else if (ch2 == 'g') {
687
word = word.append(generation);
688
sawg = true;
689
ix++;
690
continue;
691
} else if (ch2 == 'u') {
692
word = word.append(unique);
693
sawu = true;
694
ix++;
695
continue;
696
} else if (ch2 == '%') {
697
word = word.append('%');
698
ix++;
699
continue;
700
}
701
}
702
word = word.append(ch);
703
}
704
prev = elem;
705
}
706
707
if (count > 1 && !sawg) {
708
word = word.append('.').append(generation);
709
}
710
if (unique > 0 && !sawu) {
711
word = word.append('.').append(unique);
712
}
713
if (word.length() > 0) {
714
String n = word.toString();
715
Path p = prev == null ? Paths.get(n) : prev.resolveSibling(n);
716
result = result == null ? p : result.resolve(p);
717
} else if (result == null) {
718
result = Paths.get("");
719
}
720
721
if (path.getRoot() == null) {
722
return result.toFile();
723
} else {
724
return path.getRoot().resolve(result).toFile();
725
}
726
}
727
728
/**
729
* Rotate the set of output files
730
*/
731
private synchronized void rotate() {
732
Level oldLevel = getLevel();
733
setLevel(Level.OFF);
734
735
super.close();
736
for (int i = count-2; i >= 0; i--) {
737
File f1 = files[i];
738
File f2 = files[i+1];
739
if (f1.exists()) {
740
if (f2.exists()) {
741
f2.delete();
742
}
743
f1.renameTo(f2);
744
}
745
}
746
try {
747
open(files[0], false);
748
} catch (IOException ix) {
749
// We don't want to throw an exception here, but we
750
// report the exception to any registered ErrorManager.
751
reportError(null, ix, ErrorManager.OPEN_FAILURE);
752
753
}
754
setLevel(oldLevel);
755
}
756
757
/**
758
* Format and publish a {@code LogRecord}.
759
*
760
* @param record description of the log event. A null record is
761
* silently ignored and is not published
762
*/
763
@SuppressWarnings("removal")
764
@Override
765
public synchronized void publish(LogRecord record) {
766
if (!isLoggable(record)) {
767
return;
768
}
769
super.publish(record);
770
flush();
771
if (limit > 0 && (meter.written >= limit || meter.written < 0)) {
772
// We performed access checks in the "init" method to make sure
773
// we are only initialized from trusted code. So we assume
774
// it is OK to write the target files, even if we are
775
// currently being called from untrusted code.
776
// So it is safe to raise privilege here.
777
AccessController.doPrivileged(new PrivilegedAction<Object>() {
778
@Override
779
public Object run() {
780
rotate();
781
return null;
782
}
783
});
784
}
785
}
786
787
/**
788
* Close all the files.
789
*
790
* @throws SecurityException if a security manager exists and if
791
* the caller does not have {@code LoggingPermission("control")}.
792
*/
793
@Override
794
public synchronized void close() throws SecurityException {
795
super.close();
796
// Unlock any lock file.
797
if (lockFileName == null) {
798
return;
799
}
800
try {
801
// Close the lock file channel (which also will free any locks)
802
lockFileChannel.close();
803
} catch (Exception ex) {
804
// Problems closing the stream. Punt.
805
}
806
synchronized(locks) {
807
locks.remove(lockFileName);
808
}
809
new File(lockFileName).delete();
810
lockFileName = null;
811
lockFileChannel = null;
812
}
813
814
private static class InitializationErrorManager extends ErrorManager {
815
Exception lastException;
816
@Override
817
public void error(String msg, Exception ex, int code) {
818
lastException = ex;
819
}
820
}
821
}
822
823