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 java.io.*;
29 import java.nio.channels.FileChannel;
30 import java.nio.channels.FileLock;
31 import java.security.*;
32
33 /**
34 * Simple file logging <tt>Handler</tt>.
35 * <p>
36 * The <tt>FileHandler</tt> can either write to a specified file,
37 * or it can write to a rotating set of files.
38 * <p>
39 * For a rotating set of files, as each file reaches a given size
40 * limit, it is closed, rotated out, and a new file opened.
41 * Successively older files are named by adding "0", "1", "2",
42 * etc. into the base filename.
43 * <p>
44 * By default buffering is enabled in the IO libraries but each log
45 * record is flushed out when it is complete.
46 * <p>
47 * By default the <tt>XMLFormatter</tt> class is used for formatting.
48 * <p>
49 * <b>Configuration:</b>
50 * By default each <tt>FileHandler</tt> is initialized using the following
51 * <tt>LogManager</tt> configuration properties where <tt><handler-name></tt>
120 * been specified, it will be added at the end of the filename after a dot.
121 * (This will be after any automatically added generation number.)
122 * <p>
123 * Thus if three processes were all trying to log to fred%u.%g.txt then
124 * they might end up using fred0.0.txt, fred1.0.txt, fred2.0.txt as
125 * the first file in their rotating sequences.
126 * <p>
127 * Note that the use of unique ids to avoid conflicts is only guaranteed
128 * to work reliably when using a local disk file system.
129 *
130 * @since 1.4
131 */
132
133 public class FileHandler extends StreamHandler {
134 private MeteredStream meter;
135 private boolean append;
136 private int limit; // zero => no limit.
137 private int count;
138 private String pattern;
139 private String lockFileName;
140 private FileOutputStream lockStream;
141 private File files[];
142 private static final int MAX_LOCKS = 100;
143 private static java.util.HashMap<String, String> locks = new java.util.HashMap<>();
144
145 // A metered stream is a subclass of OutputStream that
146 // (a) forwards all its output to a target stream
147 // (b) keeps track of how many bytes have been written
148 private class MeteredStream extends OutputStream {
149 OutputStream out;
150 int written;
151
152 MeteredStream(OutputStream out, int written) {
153 this.out = out;
154 this.written = written;
155 }
156
157 public void write(int b) throws IOException {
158 out.write(b);
159 written++;
160 }
161
162 public void write(byte buff[]) throws IOException {
163 out.write(buff);
164 written += buff.length;
165 }
166
167 public void write(byte buff[], int off, int len) throws IOException {
172 public void flush() throws IOException {
173 out.flush();
174 }
175
176 public void close() throws IOException {
177 out.close();
178 }
179 }
180
181 private void open(File fname, boolean append) throws IOException {
182 int len = 0;
183 if (append) {
184 len = (int)fname.length();
185 }
186 FileOutputStream fout = new FileOutputStream(fname.toString(), append);
187 BufferedOutputStream bout = new BufferedOutputStream(fout);
188 meter = new MeteredStream(bout, len);
189 setOutputStream(meter);
190 }
191
192 // Private method to configure a FileHandler from LogManager
193 // properties and/or default values as specified in the class
194 // javadoc.
195 private void configure() {
196 LogManager manager = LogManager.getLogManager();
197
198 String cname = getClass().getName();
199
200 pattern = manager.getStringProperty(cname + ".pattern", "%h/java%u.log");
201 limit = manager.getIntProperty(cname + ".limit", 0);
202 if (limit < 0) {
203 limit = 0;
204 }
205 count = manager.getIntProperty(cname + ".count", 1);
206 if (count <= 0) {
207 count = 1;
208 }
209 append = manager.getBooleanProperty(cname + ".append", false);
210 setLevel(manager.getLevelProperty(cname + ".level", Level.ALL));
211 setFilter(manager.getFilterProperty(cname + ".filter", null));
212 setFormatter(manager.getFormatterProperty(cname + ".formatter", new XMLFormatter()));
213 try {
214 setEncoding(manager.getStringProperty(cname +".encoding", null));
270 /**
271 * Initialize a <tt>FileHandler</tt> to write to the given filename,
272 * with optional append.
273 * <p>
274 * The <tt>FileHandler</tt> is configured based on <tt>LogManager</tt>
275 * properties (or their default values) except that the given pattern
276 * argument is used as the filename pattern, the file limit is
277 * set to no limit, the file count is set to one, and the append
278 * mode is set to the given <tt>append</tt> argument.
279 * <p>
280 * There is no limit on the amount of data that may be written,
281 * so use this with care.
282 *
283 * @param pattern the name of the output file
284 * @param append specifies append mode
285 * @exception IOException if there are IO problems opening the files.
286 * @exception SecurityException if a security manager exists and if
287 * the caller does not have <tt>LoggingPermission("control")</tt>.
288 * @exception IllegalArgumentException if pattern is an empty string
289 */
290 public FileHandler(String pattern, boolean append) throws IOException, SecurityException {
291 if (pattern.length() < 1 ) {
292 throw new IllegalArgumentException();
293 }
294 checkPermission();
295 configure();
296 this.pattern = pattern;
297 this.limit = 0;
298 this.count = 1;
299 this.append = append;
300 openFiles();
301 }
302
303 /**
304 * Initialize a <tt>FileHandler</tt> to write to a set of files. When
305 * (approximately) the given limit has been written to one file,
306 * another file will be opened. The output will cycle through a set
307 * of count files.
308 * <p>
309 * The <tt>FileHandler</tt> is configured based on <tt>LogManager</tt>
310 * properties (or their default values) except that the given pattern
359 * @exception SecurityException if a security manager exists and if
360 * the caller does not have <tt>LoggingPermission("control")</tt>.
361 * @exception IllegalArgumentException if limit < 0, or count < 1.
362 * @exception IllegalArgumentException if pattern is an empty string
363 *
364 */
365 public FileHandler(String pattern, int limit, int count, boolean append)
366 throws IOException, SecurityException {
367 if (limit < 0 || count < 1 || pattern.length() < 1) {
368 throw new IllegalArgumentException();
369 }
370 checkPermission();
371 configure();
372 this.pattern = pattern;
373 this.limit = limit;
374 this.count = count;
375 this.append = append;
376 openFiles();
377 }
378
379 // Private method to open the set of output files, based on the
380 // configured instance variables.
381 private void openFiles() throws IOException {
382 LogManager manager = LogManager.getLogManager();
383 manager.checkPermission();
384 if (count < 1) {
385 throw new IllegalArgumentException("file count = " + count);
386 }
387 if (limit < 0) {
388 limit = 0;
389 }
390
391 // We register our own ErrorManager during initialization
392 // so we can record exceptions.
393 InitializationErrorManager em = new InitializationErrorManager();
394 setErrorManager(em);
395
396 // Create a lock file. This grants us exclusive access
397 // to our set of output files, as long as we are alive.
398 int unique = -1;
399 for (;;) {
400 unique++;
401 if (unique > MAX_LOCKS) {
402 throw new IOException("Couldn't get lock for " + pattern);
403 }
404 // Generate a lock file name from the "unique" int.
405 lockFileName = generate(pattern, 0, unique).toString() + ".lck";
406 // Now try to lock that filename.
407 // Because some systems (e.g., Solaris) can only do file locks
408 // between processes (and not within a process), we first check
409 // if we ourself already have the file locked.
410 synchronized(locks) {
411 if (locks.get(lockFileName) != null) {
412 // We already own this lock, for a different FileHandler
413 // object. Try again.
414 continue;
415 }
416 FileChannel fc;
417 try {
418 lockStream = new FileOutputStream(lockFileName);
419 fc = lockStream.getChannel();
420 } catch (IOException ix) {
421 // We got an IOException while trying to open the file.
422 // Try the next file.
423 continue;
424 }
425 boolean available;
426 try {
427 available = fc.tryLock() != null;
428 // We got the lock OK.
429 } catch (IOException ix) {
430 // We got an IOException while trying to get the lock.
431 // This normally indicates that locking is not supported
432 // on the target directory. We have to proceed without
433 // getting a lock. Drop through.
434 available = true;
435 }
436 if (available) {
437 // We got the lock. Remember it.
438 locks.put(lockFileName, lockFileName);
439 break;
440 }
441
442 // We failed to get the lock. Try next file.
443 fc.close();
444 }
445 }
446
447 files = new File[count];
448 for (int i = 0; i < count; i++) {
449 files[i] = generate(pattern, i, unique);
450 }
451
452 // Create the initial log file.
453 if (append) {
454 open(files[0], true);
455 } else {
456 rotate();
457 }
458
459 // Did we detect any exceptions during initialization?
460 Exception ex = em.lastException;
461 if (ex != null) {
462 if (ex instanceof IOException) {
463 throw (IOException) ex;
464 } else if (ex instanceof SecurityException) {
465 throw (SecurityException) ex;
466 } else {
467 throw new IOException("Exception: " + ex);
468 }
469 }
470
471 // Install the normal default ErrorManager.
472 setErrorManager(new ErrorManager());
473 }
474
475 // Generate a filename from a pattern.
476 private File generate(String pattern, int generation, int unique) throws IOException {
477 File file = null;
478 String word = "";
479 int ix = 0;
480 boolean sawg = false;
481 boolean sawu = false;
482 while (ix < pattern.length()) {
483 char ch = pattern.charAt(ix);
484 ix++;
485 char ch2 = 0;
486 if (ix < pattern.length()) {
487 ch2 = Character.toLowerCase(pattern.charAt(ix));
488 }
489 if (ch == '/') {
490 if (file == null) {
491 file = new File(word);
492 } else {
493 file = new File(file, word);
494 }
495 word = "";
496 continue;
531 }
532 }
533 word = word + ch;
534 }
535 if (count > 1 && !sawg) {
536 word = word + "." + generation;
537 }
538 if (unique > 0 && !sawu) {
539 word = word + "." + unique;
540 }
541 if (word.length() > 0) {
542 if (file == null) {
543 file = new File(word);
544 } else {
545 file = new File(file, word);
546 }
547 }
548 return file;
549 }
550
551 // Rotate the set of output files
552 private synchronized void rotate() {
553 Level oldLevel = getLevel();
554 setLevel(Level.OFF);
555
556 super.close();
557 for (int i = count-2; i >= 0; i--) {
558 File f1 = files[i];
559 File f2 = files[i+1];
560 if (f1.exists()) {
561 if (f2.exists()) {
562 f2.delete();
563 }
564 f1.renameTo(f2);
565 }
566 }
567 try {
568 open(files[0], false);
569 } catch (IOException ix) {
570 // We don't want to throw an exception here, but we
571 // report the exception to any registered ErrorManager.
598 rotate();
599 return null;
600 }
601 });
602 }
603 }
604
605 /**
606 * Close all the files.
607 *
608 * @exception SecurityException if a security manager exists and if
609 * the caller does not have <tt>LoggingPermission("control")</tt>.
610 */
611 public synchronized void close() throws SecurityException {
612 super.close();
613 // Unlock any lock file.
614 if (lockFileName == null) {
615 return;
616 }
617 try {
618 // Closing the lock file's FileOutputStream will close
619 // the underlying channel and free any locks.
620 lockStream.close();
621 } catch (Exception ex) {
622 // Problems closing the stream. Punt.
623 }
624 synchronized(locks) {
625 locks.remove(lockFileName);
626 }
627 new File(lockFileName).delete();
628 lockFileName = null;
629 lockStream = null;
630 }
631
632 private static class InitializationErrorManager extends ErrorManager {
633 Exception lastException;
634 public void error(String msg, Exception ex, int code) {
635 lastException = ex;
636 }
637 }
638
639 // Private native method to check if we are in a set UID program.
640 private static native boolean isSetUID();
641 }
|
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.CREATE_NEW;
29 import static java.nio.file.StandardOpenOption.WRITE;
30
31 import java.io.BufferedOutputStream;
32 import java.io.File;
33 import java.io.FileOutputStream;
34 import java.io.IOException;
35 import java.io.OutputStream;
36 import java.nio.channels.FileChannel;
37 import java.nio.file.FileAlreadyExistsException;
38 import java.nio.file.Paths;
39 import java.security.AccessController;
40 import java.security.PrivilegedAction;
41
42 /**
43 * Simple file logging <tt>Handler</tt>.
44 * <p>
45 * The <tt>FileHandler</tt> can either write to a specified file,
46 * or it can write to a rotating set of files.
47 * <p>
48 * For a rotating set of files, as each file reaches a given size
49 * limit, it is closed, rotated out, and a new file opened.
50 * Successively older files are named by adding "0", "1", "2",
51 * etc. into the base filename.
52 * <p>
53 * By default buffering is enabled in the IO libraries but each log
54 * record is flushed out when it is complete.
55 * <p>
56 * By default the <tt>XMLFormatter</tt> class is used for formatting.
57 * <p>
58 * <b>Configuration:</b>
59 * By default each <tt>FileHandler</tt> is initialized using the following
60 * <tt>LogManager</tt> configuration properties where <tt><handler-name></tt>
129 * been specified, it will be added at the end of the filename after a dot.
130 * (This will be after any automatically added generation number.)
131 * <p>
132 * Thus if three processes were all trying to log to fred%u.%g.txt then
133 * they might end up using fred0.0.txt, fred1.0.txt, fred2.0.txt as
134 * the first file in their rotating sequences.
135 * <p>
136 * Note that the use of unique ids to avoid conflicts is only guaranteed
137 * to work reliably when using a local disk file system.
138 *
139 * @since 1.4
140 */
141
142 public class FileHandler extends StreamHandler {
143 private MeteredStream meter;
144 private boolean append;
145 private int limit; // zero => no limit.
146 private int count;
147 private String pattern;
148 private String lockFileName;
149 private FileChannel lockFileChannel;
150 private File files[];
151 private static final int MAX_LOCKS = 100;
152 private static java.util.HashMap<String, String> locks = new java.util.HashMap<>();
153
154 /**
155 * A metered stream is a subclass of OutputStream that
156 * (a) forwards all its output to a target stream
157 * (b) keeps track of how many bytes have been written
158 */
159 private class MeteredStream extends OutputStream {
160 OutputStream out;
161 int written;
162
163 MeteredStream(OutputStream out, int written) {
164 this.out = out;
165 this.written = written;
166 }
167
168 public void write(int b) throws IOException {
169 out.write(b);
170 written++;
171 }
172
173 public void write(byte buff[]) throws IOException {
174 out.write(buff);
175 written += buff.length;
176 }
177
178 public void write(byte buff[], int off, int len) throws IOException {
183 public void flush() throws IOException {
184 out.flush();
185 }
186
187 public void close() throws IOException {
188 out.close();
189 }
190 }
191
192 private void open(File fname, boolean append) throws IOException {
193 int len = 0;
194 if (append) {
195 len = (int)fname.length();
196 }
197 FileOutputStream fout = new FileOutputStream(fname.toString(), append);
198 BufferedOutputStream bout = new BufferedOutputStream(fout);
199 meter = new MeteredStream(bout, len);
200 setOutputStream(meter);
201 }
202
203 /**
204 * Configure a FileHandler from LogManager properties and/or default values
205 * as specified in the class javadoc.
206 */
207 private void configure() {
208 LogManager manager = LogManager.getLogManager();
209
210 String cname = getClass().getName();
211
212 pattern = manager.getStringProperty(cname + ".pattern", "%h/java%u.log");
213 limit = manager.getIntProperty(cname + ".limit", 0);
214 if (limit < 0) {
215 limit = 0;
216 }
217 count = manager.getIntProperty(cname + ".count", 1);
218 if (count <= 0) {
219 count = 1;
220 }
221 append = manager.getBooleanProperty(cname + ".append", false);
222 setLevel(manager.getLevelProperty(cname + ".level", Level.ALL));
223 setFilter(manager.getFilterProperty(cname + ".filter", null));
224 setFormatter(manager.getFormatterProperty(cname + ".formatter", new XMLFormatter()));
225 try {
226 setEncoding(manager.getStringProperty(cname +".encoding", null));
282 /**
283 * Initialize a <tt>FileHandler</tt> to write to the given filename,
284 * with optional append.
285 * <p>
286 * The <tt>FileHandler</tt> is configured based on <tt>LogManager</tt>
287 * properties (or their default values) except that the given pattern
288 * argument is used as the filename pattern, the file limit is
289 * set to no limit, the file count is set to one, and the append
290 * mode is set to the given <tt>append</tt> argument.
291 * <p>
292 * There is no limit on the amount of data that may be written,
293 * so use this with care.
294 *
295 * @param pattern the name of the output file
296 * @param append specifies append mode
297 * @exception IOException if there are IO problems opening the files.
298 * @exception SecurityException if a security manager exists and if
299 * the caller does not have <tt>LoggingPermission("control")</tt>.
300 * @exception IllegalArgumentException if pattern is an empty string
301 */
302 public FileHandler(String pattern, boolean append) throws IOException,
303 SecurityException {
304 if (pattern.length() < 1 ) {
305 throw new IllegalArgumentException();
306 }
307 checkPermission();
308 configure();
309 this.pattern = pattern;
310 this.limit = 0;
311 this.count = 1;
312 this.append = append;
313 openFiles();
314 }
315
316 /**
317 * Initialize a <tt>FileHandler</tt> to write to a set of files. When
318 * (approximately) the given limit has been written to one file,
319 * another file will be opened. The output will cycle through a set
320 * of count files.
321 * <p>
322 * The <tt>FileHandler</tt> is configured based on <tt>LogManager</tt>
323 * properties (or their default values) except that the given pattern
372 * @exception SecurityException if a security manager exists and if
373 * the caller does not have <tt>LoggingPermission("control")</tt>.
374 * @exception IllegalArgumentException if limit < 0, or count < 1.
375 * @exception IllegalArgumentException if pattern is an empty string
376 *
377 */
378 public FileHandler(String pattern, int limit, int count, boolean append)
379 throws IOException, SecurityException {
380 if (limit < 0 || count < 1 || pattern.length() < 1) {
381 throw new IllegalArgumentException();
382 }
383 checkPermission();
384 configure();
385 this.pattern = pattern;
386 this.limit = limit;
387 this.count = count;
388 this.append = append;
389 openFiles();
390 }
391
392 /**
393 * Open the set of output files, based on the configured
394 * instance variables.
395 */
396 private void openFiles() throws IOException {
397 LogManager manager = LogManager.getLogManager();
398 manager.checkPermission();
399 if (count < 1) {
400 throw new IllegalArgumentException("file count = " + count);
401 }
402 if (limit < 0) {
403 limit = 0;
404 }
405
406 // We register our own ErrorManager during initialization
407 // so we can record exceptions.
408 InitializationErrorManager em = new InitializationErrorManager();
409 setErrorManager(em);
410
411 // Create a lock file. This grants us exclusive access
412 // to our set of output files, as long as we are alive.
413 int unique = -1;
414 for (;;) {
415 unique++;
416 if (unique > MAX_LOCKS) {
417 throw new IOException("Couldn't get lock for " + pattern);
418 }
419 // Generate a lock file name from the "unique" int.
420 lockFileName = generate(pattern, 0, unique).toString() + ".lck";
421 // Now try to lock that filename.
422 // Because some systems (e.g., Solaris) can only do file locks
423 // between processes (and not within a process), we first check
424 // if we ourself already have the file locked.
425 synchronized(locks) {
426 if (locks.get(lockFileName) != null) {
427 // We already own this lock, for a different FileHandler
428 // object. Try again.
429 continue;
430 }
431
432 try {
433 lockFileChannel = FileChannel.open(Paths.get(lockFileName),
434 CREATE_NEW, WRITE);
435 } catch (FileAlreadyExistsException ix) {
436 // try the next lock file name in the sequence
437 continue;
438 }
439
440 boolean available;
441 try {
442 available = lockFileChannel.tryLock() != null;
443 // We got the lock OK.
444 } catch (IOException ix) {
445 // We got an IOException while trying to get the lock.
446 // This normally indicates that locking is not supported
447 // on the target directory. We have to proceed without
448 // getting a lock. Drop through.
449 available = true;
450 }
451 if (available) {
452 // We got the lock. Remember it.
453 locks.put(lockFileName, lockFileName);
454 break;
455 }
456
457 // We failed to get the lock. Try next file.
458 lockFileChannel.close();
459 }
460 }
461
462 files = new File[count];
463 for (int i = 0; i < count; i++) {
464 files[i] = generate(pattern, i, unique);
465 }
466
467 // Create the initial log file.
468 if (append) {
469 open(files[0], true);
470 } else {
471 rotate();
472 }
473
474 // Did we detect any exceptions during initialization?
475 Exception ex = em.lastException;
476 if (ex != null) {
477 if (ex instanceof IOException) {
478 throw (IOException) ex;
479 } else if (ex instanceof SecurityException) {
480 throw (SecurityException) ex;
481 } else {
482 throw new IOException("Exception: " + ex);
483 }
484 }
485
486 // Install the normal default ErrorManager.
487 setErrorManager(new ErrorManager());
488 }
489
490 /**
491 * Generate a file based on a user-supplied pattern, generation number,
492 * and an integer uniqueness suffix
493 * @param pattern the pattern for naming the output file
494 * @param generation the generation number to distinguish rotated logs
495 * @param unique a unique number to resolve conflicts
496 * @return the generated File
497 * @throws IOException
498 */
499 private File generate(String pattern, int generation, int unique)
500 throws IOException {
501 File file = null;
502 String word = "";
503 int ix = 0;
504 boolean sawg = false;
505 boolean sawu = false;
506 while (ix < pattern.length()) {
507 char ch = pattern.charAt(ix);
508 ix++;
509 char ch2 = 0;
510 if (ix < pattern.length()) {
511 ch2 = Character.toLowerCase(pattern.charAt(ix));
512 }
513 if (ch == '/') {
514 if (file == null) {
515 file = new File(word);
516 } else {
517 file = new File(file, word);
518 }
519 word = "";
520 continue;
555 }
556 }
557 word = word + ch;
558 }
559 if (count > 1 && !sawg) {
560 word = word + "." + generation;
561 }
562 if (unique > 0 && !sawu) {
563 word = word + "." + unique;
564 }
565 if (word.length() > 0) {
566 if (file == null) {
567 file = new File(word);
568 } else {
569 file = new File(file, word);
570 }
571 }
572 return file;
573 }
574
575 /**
576 * Rotate the set of output files
577 */
578 private synchronized void rotate() {
579 Level oldLevel = getLevel();
580 setLevel(Level.OFF);
581
582 super.close();
583 for (int i = count-2; i >= 0; i--) {
584 File f1 = files[i];
585 File f2 = files[i+1];
586 if (f1.exists()) {
587 if (f2.exists()) {
588 f2.delete();
589 }
590 f1.renameTo(f2);
591 }
592 }
593 try {
594 open(files[0], false);
595 } catch (IOException ix) {
596 // We don't want to throw an exception here, but we
597 // report the exception to any registered ErrorManager.
624 rotate();
625 return null;
626 }
627 });
628 }
629 }
630
631 /**
632 * Close all the files.
633 *
634 * @exception SecurityException if a security manager exists and if
635 * the caller does not have <tt>LoggingPermission("control")</tt>.
636 */
637 public synchronized void close() throws SecurityException {
638 super.close();
639 // Unlock any lock file.
640 if (lockFileName == null) {
641 return;
642 }
643 try {
644 // Close the lock file channel (which also will free any locks)
645 lockFileChannel.close();
646 } catch (Exception ex) {
647 // Problems closing the stream. Punt.
648 }
649 synchronized(locks) {
650 locks.remove(lockFileName);
651 }
652 new File(lockFileName).delete();
653 lockFileName = null;
654 lockFileChannel = null;
655 }
656
657 private static class InitializationErrorManager extends ErrorManager {
658 Exception lastException;
659 public void error(String msg, Exception ex, int code) {
660 lastException = ex;
661 }
662 }
663
664 /**
665 * check if we are in a set UID program.
666 */
667 private static native boolean isSetUID();
668 }
|