KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > jts > CosTransactions > LogHandle


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23
24 /*
25  * Copyright 2004-2005 Sun Microsystems, Inc. All rights reserved.
26  * Use is subject to license terms.
27  */

28 //----------------------------------------------------------------------------
29
//
30
// Module: LogHandle.java
31
//
32
// Description: Log file handle.
33
//
34
// Product: com.sun.jts.CosTransactions
35
//
36
// Author: Simon Holdsworth
37
//
38
// Date: March, 1997
39
//
40
// Copyright (c): 1995-1997 IBM Corp.
41
//
42
// The source code for this program is not published or otherwise divested
43
// of its trade secrets, irrespective of what has been deposited with the
44
// U.S. Copyright Office.
45
//
46
// This software contains confidential and proprietary information of
47
// IBM Corp.
48
//----------------------------------------------------------------------------
49

50 package com.sun.jts.CosTransactions;
51
52 // Import required classes.
53

54 import java.util.*;
55 import java.io.*;
56
57 /**A class containing attributes of an open log file.
58  *
59  * @version 0.01
60  *
61  * @author Simon Holdsworth, IBM Corporation
62  *
63  * @see
64 */

65 //----------------------------------------------------------------------------
66
// CHANGE HISTORY
67
//
68
// Version By Change Description
69
// 0.01 SAJH Initial implementation.
70
//-----------------------------------------------------------------------------
71

72 class LogHandle {
73
74     // WriteMode identifies the mode in which a system journal record
75
// is to be written, and affects the performance overhead of the write.
76

77     /**Buffer the data and return (minimal overhead);
78      */

79     final static int BUFFER = 0;
80
81     /**Flush and force the data to permanent storage before returning (high
82      * overhead) - ie to physically write the record.
83      */

84     final static int FORCE = 1;
85
86     // This type enumerates the options when truncating a log file.
87

88     /**Don't include tail LSN
89      */

90     final static int TAIL_NOT_INCLUSIVE = 0;
91
92     /**Include tail LSN
93      */

94     final static int TAIL_INCLUSIVE = 1;
95
96     // Records written to the Master Log are allocated log record types
97

98     /**Start-of-checkpoint record - internally generated by &damjo.
99      */

100     final static int START_CKPT = 0;
101
102     /**Checkpoint record from an individual registered module
103      */

104     final static int INDV_CKPT = 1;
105
106     /**End-of-checkpoint record - internally generated by &damjo
107      */

108     final static int END_CKPT = 2;
109
110     /**Record of a newly opened journal
111      */

112     final static int NEW_JRNL = 3;
113
114     /**Upper limit for user specified record type value.
115      */

116     final static int RECORD_TYPE_MAX = 0xFFFF;
117
118     /**Record type written to local system logs to indicate the position
119      * corresponding to the start of the last successful checkpoint.
120      */

121     final static int MARKER = RECORD_TYPE_MAX;
122
123     /**The record type written to at the end of an extent to signify that the
124      * log record is a link record (a dummy log record).
125      */

126     final static int LINK = RECORD_TYPE_MAX + 1;
127
128     // Constants used for log files.
129

130     /**The maximum number of extents which can be created for a log file.
131      */

132     final static int MAX_NUMBER_EXTENTS = 0xFFFFFFFF;
133
134     /**Number of log write operations which will be performed before forcing
135      * the control data to permanent storage
136      */

137     // final static int CONTROL_FORCE_INTERVAL = 20;
138
final static int CONTROL_FORCE_INTERVAL = 100;
139
140     /**This determines the size of the largest log record which can be written.
141      */

142     final static int MAX_EXTENT_SIZE = LogFileHandle.FILESYSTEM_BLOCKSIZE*16;
143
144     /**This is the size of the cushion file used to find if the log is
145      * short on space.
146      */

147     final static int CUSHION_SIZE = MAX_EXTENT_SIZE;
148
149     /**The length of the name assigned to a logfile. This is restricted to
150      * 8 to support the FAT file system.
151      */

152     final static int NAME_LENGTH = 8;
153
154     /**The maximum number of names available to be assigned for logfiles.
155      * The name is made up of LOG_FILENAME_PREFIX which is 5 characters
156      * followed by a 3 digit hex extension.
157      */

158     final static int MAX_NAMES = 4096;
159
160     /**The length of the fixed filename prefix used when allocating new
161      * log file names.
162      */

163     final static int FILENAME_PREFIX_LEN = 5;
164
165     /**The number of entries in each log file descriptor. It is used for
166      * performance reason, so we can get to the extent descriptor quickly.
167      */

168     final static int EXTENT_TABLE_SIZE = 16;
169
170     /**This is used to give the maximum length of a log file name, which also
171      * inclues the NULL terminator. 200 for the logname was derived from :
172      * /var/cics_regions/region_name/log/<logname>.extent.00000001
173      * We know the maximum region name is 8 chars, therefore every character
174      * except the logname added upto 46. Hence 255 (AIX path max) - 46 is 209,
175      * however 200 is a nice round (and large number).
176      */

177     //final static int NAME_MAX_SIZE = 200;
178

179     /**This is the reason why we are calling the calling back function.
180      */

181     final static int CALLBACK_REASON_SOS = 1;
182
183     /**The offset in the control file for the first restart data record.
184      */

185     final static int RESTART_OFFSET_1 = LogFileHandle.FILESYSTEM_BLOCKSIZE;
186
187     /**The offset in the control file for the second restart data record.
188      */

189     final static int RESTART_OFFSET_2 = LogFileHandle.FILESYSTEM_BLOCKSIZE*5;
190
191     /**This is the maximum size of a log record.
192      */

193     final static int MAX_RECORD_SIZE = MAX_EXTENT_SIZE - 2*LogRecordHeader.SIZEOF - 2*LogRecordEnding.SIZEOF;
194
195     /**The maximum size of a restart record
196      */

197     final static int MAX_RESTART_SIZE = LogFileHandle.FILESYSTEM_BLOCKSIZE*4 - 2*LogRestartDescriptor.SIZEOF;
198
199     /**The size of a control file which is allocated at open time.
200      */

201     final static int CONTROL_FILE_SIZE = RESTART_OFFSET_2 + MAX_RESTART_SIZE + 2*LogRestartDescriptor.SIZEOF;
202
203     /**The size of a chunk to allocate from the disk space
204      */

205     final static int ALLOCATE_SIZE = MAX_EXTENT_SIZE;
206
207     // Instance members
208

209     LogHandle blockValid = null;
210     int restartDataLength = 0;
211     int recordsWritten = 0;
212     int chunkRemaining = 0;
213     int activeRestartVersion = 0;
214     LogUpcallTarget upcallTarget = null;
215     ArrayList cursors = null;
216     boolean cushionExists = false;
217     boolean upcallInProgress = false;
218     Hashtable extentTable = null;
219     String JavaDoc logFileName = null;
220     LogFileHandle logFileHandle = null;
221     LogControlDescriptor logControlDescriptor = null;
222     LogControl logControl = null;
223
224
225     /**Creates a LogHandle object for the given log instance.
226      *
227      * @param control The log instance.
228      * @param logName The name of the log.
229      * @param controlFH The handle of the control file.
230      * @param upcall The log upcall.
231      *
232      * @return
233      *
234      * @exception LogException The creation failed.
235      *
236      * @see
237      */

238     LogHandle( LogControl control,
239                String JavaDoc logName,
240                LogFileHandle controlFH,
241                LogUpcallTarget upcall )
242         throws LogException {
243
244         // Initialise instance members.
245

246         logFileName = new String JavaDoc(logName);
247         upcallTarget = upcall;
248         logControl = control;
249         logFileHandle = controlFH;
250         blockValid = this;
251         logControlDescriptor = new LogControlDescriptor();
252         cursors = new ArrayList();
253         extentTable = new Hashtable(EXTENT_TABLE_SIZE);
254
255     }
256
257     /**Writes a record to the log.
258      *
259      * @param record The log record.
260      * @param recordType The log record type.
261      * @param writeMode The write mode.
262      *
263      * @return The LSN of the written record
264      *
265      * @exception LogException The write failed.
266      *
267      * @see
268      */

269     synchronized LogLSN writeRecord( byte[] record,
270                                      int recordType,
271                                      int writeMode )
272         throws LogException {
273
274         // Check BlockValid field in Log_FileDescriptor block pointed to
275
// by logHandle parameter, and ensure it is valid
276
// IF not valid Log_FileDescriptor
277
// Return LOG_INVALID_FILE_DESCRIPTOR
278

279         if( blockValid != this )
280             throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);
281
282         // IF not LogInitialised
283
// Return LOG_NOT_INITIALISED
284

285         if( !logControl.logInitialised )
286             throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);
287
288         // IF ReadOnly log
289
// Return LOG_READ_ONLY_ACCESS
290

291         if( logControl.logReadOnly )
292             throw new LogException(null,LogException.LOG_READ_ONLY_ACCESS,3);
293
294         // Sanity check the recordType and writeMode parameters
295

296         if( recordType > RECORD_TYPE_MAX )
297             throw new LogException(null,LogException.LOG_INVALID_RECORDTYPE,5);
298
299         if( writeMode != FORCE && writeMode != BUFFER )
300             throw new LogException(null,LogException.LOG_INVALID_WRITEMODE,6);
301
302         // Calculate the total size of the log record by totalling size of all
303
// input buffers together with the record header and record ending
304

305         int recordSize = record.length + LogRecordHeader.SIZEOF + LogRecordEnding.SIZEOF;
306
307
308         // IF the log record data is greater than LOG_MAX_LOG_RECORD_SIZE
309
// Unlock the log file latch
310
// Return LOG_RECORD_TOO_LARGE
311

312         if( recordSize > MAX_RECORD_SIZE )
313             throw new LogException(null,LogException.LOG_RECORD_TOO_LARGE,7);
314
315         // Calculate the remaining space in the current extent by subtracting
316
// (log head LSN's offset + 2*LOG_HEADER_SIZE + LOG_ENDING_SIZE) from
317
// LOG_MAX_EXTENT_SIZE
318

319         int remainingSpace = MAX_EXTENT_SIZE - ( logControlDescriptor.nextLSN.offset
320                                                  + 2*LogRecordHeader.SIZEOF
321                                                  + LogRecordEnding.SIZEOF );
322
323
324         // Position the file pointer to the next free location
325
// NOTE: either the record or a link record will be wrote here
326
// Set the WORKING extent descriptor to the returned value
327
//
328
// IF an error occurs let it go to the caller.
329

330         LogExtent logEDP = positionFilePointer(logControlDescriptor.nextLSN,0,LogExtent.ACCESSTYPE_WRITE);
331
332         // IF not enough space in current extent
333

334         if( remainingSpace < recordSize ) {
335             LogRecordHeader link = new LogRecordHeader();
336
337             // Calculate the number of the next (new) extent
338
// Calculate LSN of first record in the new extent.
339
// Test that the new extent number has not wrapped to become negative;
340
// if it has, throw an exception.
341

342             int nextExtent = logControlDescriptor.headLSN.extent+1;
343             if( nextExtent < 0 )
344                 throw new LogException(null,LogException.LOG_WRITE_FAILURE,8);
345
346             // If the new extent file is already open, there is nothing we can do but
347
// fail. We cannot run the short-on-storage upcall to try to free the
348
// extent as the upcall needs to write information to the offending extent.
349

350             if( extentTable.containsKey(new Integer JavaDoc(LogExtent.modExtent(nextExtent))) )
351                 throw new LogException(null,LogException.LOG_WRITE_FAILURE,9);
352
353             // Create link record containing
354
// - the LSN of the link record (i.e. its own LSN)
355
// - the LSN of the previous log record (log head LSN from
356
// Log_FileDescriptor block)
357
// - the LSN of the next log record (this is the LSN of the
358
// first record in new extent file
359

360             link.recordType = LINK;
361             link.previousLSN = new LogLSN(logControlDescriptor.headLSN);
362             link.currentLSN = new LogLSN(logControlDescriptor.nextLSN);
363             link.nextLSN = new LogLSN(nextExtent,0);
364
365             // Move a file pointer to the next record position
366

367             LogExtent nextEDP = positionFilePointer(link.nextLSN,0,LogExtent.ACCESSTYPE_WRITE);
368
369             // Issue WRITE to add link record to the 'full' extent file
370
// IF the WRITE fails
371
// Close the new extent file
372
// Unchain its extent descriptor block from the hash table
373
// Deallocate the extent descriptor block
374
// Unlock the log file latch
375
// Return LOG_WRITE_FAILURE
376

377             byte[] linkBytes = new byte[link.SIZEOF];
378             link.toBytes(linkBytes,0);
379             int bytesWritten = 0;
380             try {
381                 bytesWritten = logEDP.fileHandle.fileWrite(linkBytes);
382             } catch( LogException le ) {
383                 extentTable.remove(new Integer JavaDoc(logControlDescriptor.headLSN.extent));
384                 nextEDP.finalize();
385                 throw new LogException(null,LogException.LOG_WRITE_FAILURE,10);
386             }
387
388             // Set its 'extent written' flag to TRUE
389

390             logEDP.writtenSinceLastForce = true;
391             logEDP.cursorPosition += bytesWritten;
392
393             // Update the head LSN value in the Log_FileDescriptor block
394
// with the LSN of the link record
395
// Update the next LSN value in the Log_FileDescriptor block
396
// with the LSN of the first block in the new extent
397

398             logControlDescriptor.headLSN.copy(link.currentLSN);
399             logControlDescriptor.nextLSN.copy(link.nextLSN);
400
401             // Set the WORKING extent descriptor to the new/next extent
402

403             logEDP = nextEDP;
404
405             // Set the ChunkRemaining to Zero
406

407             chunkRemaining = 0;
408         }
409
410         // Use the offset value from the next LSN to calculate the next free offset
411
// in the extent file
412
// Calculate the 'next free' LSN
413

414         LogLSN nextFree = new LogLSN(logControlDescriptor.nextLSN.extent,
415                                      logControlDescriptor.nextLSN.offset + recordSize);
416
417         // Build the record header, initialising with
418
// - log record type (recordType passed as input parameter)
419
// - log record length (cumulative length of all data buffers)
420
// - the LSN of the previous log record (PreviousRecord; log head LSN from
421
// Log_FileDescriptor block)
422
// - the LSN of the next log record (NextRecord; the 'next free' LSN value)
423
// - the LSN of the record about to be written (ThisRecord)
424

425         LogRecordHeader logRH = new LogRecordHeader();
426
427         logRH.recordType = recordType;
428         logRH.recordLength = record.length;
429         logRH.nextLSN = nextFree;
430         logRH.previousLSN = new LogLSN(logControlDescriptor.headLSN);
431         logRH.currentLSN = new LogLSN(logControlDescriptor.nextLSN);
432
433         // Build the record ending, initialising with
434
// the LSN of the record about to be written (ThisRecord)
435

436         LogRecordEnding logRE = new LogRecordEnding();
437
438         logRE.currentLSN = logRH.currentLSN;
439
440
441         // Initialise an array of iovec structures ready for a WRITEV request
442
// (an iovec structure specifies the base address and length of an area in
443
// memory from which data should be written)
444
// - set the first element to point to the record header, set iovCount=1
445
// - LOOP for each buffer in recordPtrList
446
// initialise next iovec element with its address and length
447
// increment iovCount
448
// ENDLOOP
449
// - set the next element to point to the record ending, increment iovCount
450

451         byte[] writeBytes = new byte[LogRecordHeader.SIZEOF+record.length+LogRecordEnding.SIZEOF];
452
453         logRH.toBytes(writeBytes,0);
454         System.arraycopy(record,0,writeBytes,LogRecordHeader.SIZEOF,record.length);
455         logRE.toBytes(writeBytes,LogRecordHeader.SIZEOF+record.length);
456
457         // IF there is enough space in current chunk
458
// Decrease ChunkRemaining by RecordSize
459

460         boolean cushionFreed = false;
461
462         if( chunkRemaining > recordSize )
463             chunkRemaining -= recordSize;
464         else {
465
466             // CALCULATE the size of disk space to grab
467

468             int grabSize = chunkRemaining + ALLOCATE_SIZE;
469
470             // IF there is NOT enough space in current extent
471
// Set the Grab size to be the size of the remaining extent
472

473             if( grabSize + logControlDescriptor.nextLSN.offset > MAX_EXTENT_SIZE )
474                 grabSize = MAX_EXTENT_SIZE - logControlDescriptor.nextLSN.offset;
475
476
477             // Set the Allocate success flag to FALSE;
478

479             boolean allocateSuccess = false;
480
481             do {
482                 // ALLOCATE the Grab size of disk space
483
// IF successful
484
// Set AllocateSuccess to TRUE
485
// BREAK
486

487                 try {
488                     logEDP.fileHandle.allocFileStorage(grabSize);
489                 } catch( LogException le ) {
490
491                     // IF the request fails due to lack of storage, i.e.
492
// ENOSPC - insufficient space left in file system or
493
// EDQUOT - user or group disk block quota reached
494
// Call the Log_FreeCushion routine
495
// IF there was no cushion to free
496
// Unlock the log file latch
497
// Return LOG_NO_SPACE
498
// Move the File pointer back to it's original offset
499
// ELSE
500
// EXIT LOOP with 'Allocate unsuccessful' status
501

502                     if( le.errorCode == LogException.LOG_NO_SPACE ) {
503                         if( cushionExists ) {
504                             freeCushion();
505                             cushionFreed = true;
506                         } else {
507                             if( cushionFreed )
508                                 restoreCushion(false);
509
510                             throw new LogException(null,LogException.LOG_NO_SPACE,11);
511                         }
512
513                         try {
514                             logEDP = positionFilePointer(logControlDescriptor.nextLSN,0,LogExtent.ACCESSTYPE_WRITE);
515                         } catch( Throwable JavaDoc e ) {};
516                     }
517                     else
518                         allocateSuccess = false;
519                 }
520                 allocateSuccess = true;
521             }
522             while( !allocateSuccess );
523
524             // IF allocate failed
525
// Unlock the log file latch
526
// Return LOG_WRITE_FAILURE
527

528             if( !allocateSuccess )
529                 throw new LogException(null,LogException.LOG_WRITE_FAILURE,12);
530
531             // SET ChunkRemaining to the Grabbed size - RecordSize
532

533             chunkRemaining = grabSize - recordSize;
534         }
535
536         // Issue a WRITEV request to the extent file, specifying the iovec array
537
// and iovCount as input
538
// IF write failed return the error.
539

540         int bytesWritten = logEDP.fileHandle.fileWrite(writeBytes);
541
542         // Set 'extent written' flag to TRUE
543

544         logEDP.writtenSinceLastForce = true;
545         logEDP.cursorPosition += bytesWritten;
546
547         // IF LOG_FORCE was specified
548
// LOOP through each extent chain in the hash table
549
// IF 'extent written' flag is TRUE
550
// Issue FSYNC for extent file descriptor
551
// IF not successful
552
// Unlock the log file latch
553
// Return LOG_ERROR_FORCING_LOG
554
// Set 'extent written' flag to FALSE
555
// ENDLOOP
556

557         if( writeMode == FORCE ) {
558             Enumeration extents = extentTable.elements();
559             while( extents.hasMoreElements() ) {
560                 LogExtent nextEDP = (LogExtent)extents.nextElement();
561                 if( nextEDP.writtenSinceLastForce )
562                     try {
563                         nextEDP.fileHandle.fileSync();
564                         nextEDP.writtenSinceLastForce = false;
565                     } catch( LogException le ) {
566                         throw new LogException(null,LogException.LOG_ERROR_FORCING_LOG,14);
567                     }
568             }
569         }
570
571         // Update the head LSN and 'next free' LSN in the Log_FileDescriptor
572
// block
573

574         logControlDescriptor.headLSN.copy(logRH.currentLSN);
575         logControlDescriptor.nextLSN.copy(logRH.nextLSN);
576
577         // Increment the RecordsWritten counter in Log_FileDescriptor block
578

579         recordsWritten++;
580
581         // IF RecordsWritten = LOG_CONTROL_FORCE_INTERVAL or LOG_FORCE was specified
582
// Write the Log_ControlDescriptor structure (embedded in the
583
// Log_FileDescriptor block out to the control file (implied sync)
584
// IF not successful let the error pass to the caller.
585
// Reset the RecordsWritten counter to zero
586
// IF LogCushionOK is FALSE
587
// Call RestoreLogCushion Routine
588

589         if( recordsWritten >= CONTROL_FORCE_INTERVAL ) {
590             writeControlFile();
591             recordsWritten = 0;
592         }
593
594         if( cushionFreed )
595             restoreCushion(true);
596
597         // Return the written LSN as the result of the write operation.
598

599         LogLSN result = new LogLSN(logRH.currentLSN);
600
601
602         return result;
603     }
604
605     /**Reads a record from the log.
606      *
607      * @param readLSN The LSN of the record to be read.
608      * @param type An array with a single element which will be set to the type
609      * of the record read.
610      *
611      * @return The record read in.
612      *
613      * @exception LogException The read failed.
614      *
615      * @see
616      */

617     synchronized byte[] readRecord( LogLSN readLSN,
618                                     int[/*1*/] type )
619         throws LogException {
620
621         // Check BlockValid field in Log_FileDescriptor block pointed to
622
// by logHandle parameter, and ensure it is valid
623
// IF not valid Log_FileDescriptor
624
// Return LOG_INVALID_FILE_DESCRIPTOR
625

626         if( blockValid != this )
627             throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);
628
629         // IF not LogInitialised
630
// Return LOG_NOT_INITIALISED
631

632         if( !logControl.logInitialised )
633             throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);
634
635         // IF the log file is empty (head LSN equal to LOG_NULL_LSN)
636
// Unlock the log file latch
637
// Return LOG_INVALID_LSN
638

639         if( logControlDescriptor.headLSN.isNULL() )
640             throw new LogException(null,LogException.LOG_INVALID_LSN,3);
641
642         // IF the lsn specified is LOG_HEAD_LSN or LOG_TAIL_LSN
643
// substitute the current head or tail LSN from the
644
// Log_ControlDescriptor structure
645
// ELSE
646
// Ensure that the lsn specified is <= current head LSN and
647
// >= current tail LSN
648
// IF lsn does not pass these checks
649
// Unlock the log file latch
650
// Return LOG_INVALID_LSN
651

652         LogLSN lsn;
653
654         if( readLSN.equals(LogLSN.HEAD_LSN) )
655             lsn = logControlDescriptor.headLSN;
656         else if( readLSN.equals(LogLSN.TAIL_LSN) )
657             lsn = logControlDescriptor.tailLSN;
658         else if( readLSN.lessThan(logControlDescriptor.tailLSN) ||
659                  readLSN.greaterThan(logControlDescriptor.headLSN) )
660             throw new LogException(null,LogException.LOG_INVALID_LSN,4);
661         else
662             lsn = readLSN;
663
664         // Position the file pointer to the LSN specified
665
// IF not successful allow the error to pass to the caller.
666

667         LogExtent logEDP = positionFilePointer(lsn,0,LogExtent.ACCESSTYPE_READ);
668
669         // Issue a READ for the log header record
670
// IF the READ was not successful
671
// Unlock the log file latch
672
// Return LOG_READ_FAILURE
673

674         byte[] headerBytes = new byte[LogRecordHeader.SIZEOF];
675         int bytesRead = 0;
676         try {
677             bytesRead = logEDP.fileHandle.fileRead(headerBytes);
678         } catch( LogException le ) {
679             logEDP.lastAccess = LogExtent.ACCESSTYPE_UNKNOWN;
680             throw new LogException(null,le.errorCode,6);
681         }
682
683         LogRecordHeader logRH = new LogRecordHeader(headerBytes,0);
684
685         logEDP.cursorPosition += bytesRead;
686
687         // Check the record type is not a LOG_LINK_RECORD_TYPE &&
688
// the LSN in the header record is same as lsn parameter
689
// IF either test fails
690
// Unlock the log file latch
691
// Return LOG_INVALID_LSN
692

693         if( logRH.recordType == LINK ||
694             !logRH.currentLSN.equals(lsn) )
695             throw new LogException(null,LogException.LOG_INVALID_LSN,7);
696
697         // Set up a 2-element iovec array to enable the log record data and record
698
// ending to be read into a separate buffers
699
// Issue a READV request for the extent file, passing the iovec array as
700
// an input parameter
701
// IF the READV was not successful
702
// Unlock the log file latch
703
// Return LOG_READ_FAILURE
704

705         byte[][] readVect = new byte[2][];
706         readVect[0] = new byte[logRH.recordLength];
707         readVect[1] = new byte[LogRecordEnding.SIZEOF];
708
709         try {
710             bytesRead = logEDP.fileHandle.readVector(readVect);
711         } catch( LogException le ) {
712             logEDP.lastAccess = LogExtent.ACCESSTYPE_UNKNOWN;
713             throw new LogException(null,le.errorCode,9);
714         }
715
716         LogRecordEnding logRE = new LogRecordEnding(readVect[1],0);
717         logEDP.cursorPosition += bytesRead;
718
719         // IF the LSN contained in the record ending != lsn parameter
720
// Unlock the log file latch
721
// Return LOG_CORRUPTED
722

723         if( !logRE.currentLSN.equals(lsn) )
724             throw new LogException(null,LogException.LOG_CORRUPTED,10);
725
726         // Copy the returned number of bytes into the recordLengthP parameter and
727
// the record type value into the recordTypeP parameter.
728

729         type[0] = logRH.recordType;
730
731         return readVect[0];
732     }
733
734     /**Writes the restart record.
735      *
736      * @param buffer The record to be written.
737      *
738      * @return
739      *
740      * @exception LogException The write failed.
741      *
742      * @see
743      */

744     synchronized void writeRestart( byte[] buffer )
745         throws LogException {
746
747         // Check BlockValid field in Log_FileDescriptor block pointed to
748
// by logHandle parameter, and ensure it is valid
749
// IF not valid Log_FileDescriptor
750
// Return LOG_INVALID_FILE_DESCRIPTOR
751

752         if( blockValid != this )
753             throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);
754
755         // IF not LogInitialised
756
// Return LOG_NOT_INITIALISED
757

758         if( !logControl.logInitialised )
759             throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);
760
761         // IF ReadOnly log
762
// Return LOG_READ_ONLY_ACCESS
763

764         if( logControl.logReadOnly )
765             throw new LogException(null,LogException.LOG_READ_ONLY_ACCESS,3);
766
767         // IF the bufferLength parameter is greater than LOG_MAX_RESTART_RECORD_SIZE
768
// Return LOG_RECORD_TOO_LARGE
769

770         if( buffer.length > MAX_RESTART_SIZE )
771             throw new LogException(null,LogException.LOG_RECORD_TOO_LARGE,4);
772
773         // Check BlockValid field in Log_FileDescriptor block pointed to
774
// by logHandle parameter, and ensure it is still valid
775

776         if( blockValid != this )
777             throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,5);
778
779         // Use the value in ActiveRestartVersion field showing which is the active
780
// to determine which is the alternate restart record
781
// Use LSEEK to move the file pointer to its offset
782

783         int alternate = alternateRestart(activeRestartVersion);
784         int restartOffset = restartPosition(alternate);
785         logFileHandle.fileSeek(restartOffset,LogFileHandle.SEEK_ABSOLUTE);
786
787         // Initialise a Log_RestartDescriptor block with
788
// - the current file pointer offset (copied into RestartValid field)
789
// - the length of the restart data (DataLength field)
790
// - a timestamp obtained from the seconds field of a gettimer call
791

792         LogRestartDescriptor logRD = new LogRestartDescriptor();
793
794         logRD.restartDataLength = buffer.length;
795         logRD.timeStamp = (int)new Date().getTime();
796         logRD.restartValid = restartOffset;
797
798
799         // Set up a 3-element iovec array with the first element 'containing'
800
// the Log_RestartDescriptor block, the second, the supplied
801
// restart data and the third, the Log_RestartDescriptor block again.
802

803         byte[] writeBytes = new byte[LogRestartDescriptor.SIZEOF*2+buffer.length];
804
805         logRD.toBytes(writeBytes,0);
806         System.arraycopy(buffer,0,writeBytes,LogRestartDescriptor.SIZEOF,buffer.length);
807         logRD.toBytes(writeBytes,LogRestartDescriptor.SIZEOF+buffer.length);
808
809         // Issue a WRITEV request to copy the restart data to the control file
810
// IF successful
811
// Data has now been written to permanent storage, so update
812
// RestartDataLength field in Log_FileDescriptor with bufferLength
813
// and indicate (value 1 or 2) in ActiveRestartVersion field that the
814
// alternate has now become the active
815
// Return LOG_SUCCESS
816
// ELSE let the error pass to the caller.
817

818         int bytesWritten = logFileHandle.fileWrite(writeBytes);
819
820         activeRestartVersion = alternate;
821
822     }
823
824     /**Reads the restart record.
825      *
826      * @param
827      *
828      * @return The record read in.
829      *
830      * @exception LogException The read failed.
831      *
832      * @see
833      */

834     synchronized byte[] readRestart()
835         throws LogException {
836         // Check BlockValid field in Log_FileDescriptor block pointed to
837
// by logHandle parameter, and ensure it is valid
838
// IF not valid Log_FileDescriptor
839
// Return LOG_INVALID_FILE_DESCRIPTOR
840

841         if( blockValid != this )
842             throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);
843
844         // IF not LogInitialised
845
// Return LOG_NOT_INITIALISED
846

847         if( !logControl.logInitialised )
848             throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);
849
850         // Check BlockValid field in Log_FileDescriptor block pointed to
851
// by logHandle parameter, and ensure it is still valid
852

853         if( blockValid != this )
854             throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,3);
855
856         // IF there is no restart data (restart length in Log_FileDescriptor
857
// block is zero)
858
// Return LOG_NO_RESTART_RECORD
859

860         if( restartDataLength == 0 )
861             return new byte[0];
862
863         // Use the ActiveRestartVersion field in the Log_FileDescriptor block
864
// to find out which restart record is currently the active one and
865
// determine its offset within the control file
866
// Use LSEEK to move the file pointer to the start of the restart record
867
// Allow any error to pass to the caller.
868

869         int restartOffset = restartPosition(activeRestartVersion);
870         logFileHandle.fileSeek(restartOffset,LogFileHandle.SEEK_ABSOLUTE);
871
872         // Initialise an iovec array with the first element containing details of
873
// a Log_RestartDescriptor block, the second containing details of
874
// the callers buffer (bufferP and restart data length) and the third also
875
// pointing to a Log_RestartDescriptor block
876

877         byte[][] readVect = new byte[3][];
878
879         readVect[0] = new byte[LogRestartDescriptor.SIZEOF];
880         readVect[1] = new byte[restartDataLength];
881         readVect[2] = new byte[LogRestartDescriptor.SIZEOF];
882
883         // Issue a READV for the restart data
884
// IF not successful let the error pass to the caller.
885

886         int bytesRead = logFileHandle.readVector(readVect);
887
888         LogRestartDescriptor logRD = new LogRestartDescriptor(readVect[0],0);
889         LogRestartDescriptor logRDEnd = new LogRestartDescriptor(readVect[2],0);
890
891         // IF the offset value stored in the returned Log_RestartDescriptor
892
// block is not equal to the offset of the record just read OR
893
// the length held in the Log_RestartDescriptor block is not equal to
894
// the restart data length held in the Log_FileDescriptor block OR
895
// the first Log_RestartDescriptor block is not equal to the second
896
// Return LOG_CORRUPTED
897

898         if( logRD.restartValid != restartOffset ||
899             logRD.restartDataLength != restartDataLength ||
900             !logRD.equals(logRDEnd) )
901             throw new LogException(null,LogException.LOG_CORRUPTED,7);
902
903         // Copy the restart data length from Log_RestartDescriptor block into
904
// the callers recordLengthP parameter
905
// Return LOG_SUCCESS
906

907         return readVect[1];
908     }
909
910     /**Closes (and optionally deletes) the log file.
911      *
912      * @param deleteFile Indicates whether file should be deleted.
913      *
914      * @return
915      *
916      * @exception LogException The close failed.
917      *
918      * @see
919      */

920     synchronized void closeFile( boolean deleteFile ) throws LogException {
921         // Check BlockValid field in Log_FileDescriptor block and
922
// ensure it is valid
923
// IF not valid Log_FileDescriptor
924
// Return LOG_INVALID_FILE_DESCRIPTOR
925

926         if( blockValid != this )
927             throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);
928
929         // IF not LogInitialised
930
// Return LOG_NOT_INITIALISED
931

932         if( !logControl.logInitialised )
933             throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);
934
935         // Set the block valid to NULL
936

937         blockValid = null;
938
939         // LOOP for each of the 16 elements in the log file's extent hash table
940

941         boolean forced = false;
942         Enumeration extents = extentTable.elements();
943         while( extents.hasMoreElements() ) {
944             LogExtent logEDP = (LogExtent)extents.nextElement();
945
946
947             // IF extent has been written since last force
948
// Issue FSYNC for the extent's file descriptor
949
// IF not successful
950
// Return LOG_WRITE_FAILURE
951

952             if( logEDP.writtenSinceLastForce ) {
953                 logEDP.fileHandle.fileSync();
954                 logEDP.writtenSinceLastForce = false;
955                 forced = true;
956             }
957
958             // Issue a close for the extent file.
959
// Allow any error to pass to the caller.
960

961             logEDP.fileHandle.fileClose();
962
963             // If deletion of the logfile was requested, delete it.
964
//Start IASRI 4720539
965
if( deleteFile ){
966                 //if( !logEDP.file.delete() )
967
final LogExtent tmplogEDP = logEDP;
968                 Boolean JavaDoc isdeleted = (Boolean JavaDoc) java.security.AccessController.doPrivileged(
969                     new java.security.PrivilegedAction JavaDoc() {
970                         public Object JavaDoc run(){
971                             return new Boolean JavaDoc(tmplogEDP.file.delete());
972                         }
973                     }
974                 );
975                 if(!isdeleted.booleanValue())
976                     throw new LogException(null,LogException.LOG_CLOSE_FAILURE,6);
977
978             }
979             //End IASRI 4720539
980
// Address next block in chain
981
// Clear the signature in the Log_ExtentDescriptor block
982
// Deallocate the Log_ExtentDescriptor block
983

984             extentTable.remove(new Integer JavaDoc(logEDP.extentNumber));
985             logEDP.finalize();
986         }
987
988         // IF any log extents were forced (FSYNC'ed)
989
// WRITE the Log_ControlDescriptor block to the control file (with
990
// implied sync)
991
// IF not successful allow the error to pass to the caller.
992
// Return LOG_WRITE_FAILURE
993

994         if( forced && !logControl.logReadOnly )
995             writeControlFile();
996
997         // Issue CLOSE for the control file
998
// IF not successful allow the error to pass to the caller.
999

1000        logFileHandle.fileClose();
1001        logFileHandle.finalize();
1002
1003        // If deletion of the logfile was requested, delete it's
1004
// control File and the cushion file.
1005

1006        if( deleteFile ) {
1007
1008            // Delete the control file.
1009
// Start IASRI 4720539
1010
//if( !logControl.controlFile.delete() )
1011
Boolean JavaDoc isdeleted = (Boolean JavaDoc) java.security.AccessController.doPrivileged(
1012                new java.security.PrivilegedAction JavaDoc() {
1013                    public Object JavaDoc run(){
1014                        return new Boolean JavaDoc(logControl.controlFile.delete());
1015                    }
1016                }
1017            );
1018            if( !isdeleted.booleanValue() )
1019                throw new LogException(null,LogException.LOG_CLOSE_FAILURE,7);
1020            // End IASRI 4720539
1021
freeCushion();
1022
1023            // Finally remove the directory.
1024
// Start IASRI 4720539
1025
//LogControl.directory(logFileName,logControl.directoryPath).delete();
1026
java.security.AccessController.doPrivileged(
1027                new java.security.PrivilegedAction JavaDoc() {
1028                    public Object JavaDoc run(){
1029                        LogControl.directory(logFileName,logControl.directoryPath).delete();
1030                        return null;
1031                    }
1032                }
1033            );
1034            // End IASRI 4720539
1035
}
1036
1037        // Unchain the Log_FileDescriptor block from the RCA chain
1038
// the latch will be unset and terminated by Log_RemoveFileDescriptor
1039

1040        logControl.removeFile(this);
1041
1042    }
1043
1044    /**Truncates the log at the given point.
1045     *
1046     * @param truncLSN The LSN of the truncation point.
1047     * @param inclusive Indicates whether truncation includes the LSN.
1048     *
1049     * @return
1050     *
1051     * @exception LogException The operation failed.
1052     *
1053     * @see
1054     */

1055    synchronized void truncate( LogLSN truncLSN,
1056                                int inclusive )
1057        throws LogException {
1058
1059        // Check BlockValid field in Log_FileDescriptor block and
1060
// ensure it is valid
1061
// IF not valid Log_FileDescriptor
1062
// Return LOG_INVALID_FILE_DESCRIPTOR
1063

1064        if( blockValid != this )
1065            throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);
1066
1067        // IF not LogInitialised
1068
// Return LOG_NOT_INITIALISED
1069

1070        if( !logControl.logInitialised )
1071            throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);
1072
1073        // IF ReadOnly log
1074
// Return LOG_READ_ONLY_ACCESS
1075

1076        if( logControl.logReadOnly )
1077            throw new LogException(null,LogException.LOG_READ_ONLY_ACCESS,3);
1078
1079        // IF the log file is empty (head LSN = LOG_NULL_LSN) &&
1080
// the lsn value specified is not equal to LOG_HEAD_LSN
1081
// Unlock the Log_FileDescriptor latch
1082
// Return LOG_NEW_TAIL_TOO_HIGH
1083

1084        if( logControlDescriptor.headLSN.isNULL() ) {
1085            if( truncLSN.equals(LogLSN.HEAD_LSN) ) {
1086                return;
1087            } else
1088                throw new LogException(null,LogException.LOG_NEW_TAIL_TOO_HIGH,6);
1089        }
1090
1091        // IF the lsn parameter is equal to the symbolic LOG_HEAD_LSN or
1092
// the lsn parameter is equal to the actual log head LSN
1093
// Copy head LSN from Log_FileDescriptor into lsn
1094
// Remember that head of log is being truncated
1095
// ELSE IF the lsn parameter is equal to LOG_TAIL_LSN
1096
// Copy tail LSN from Log_FileDescriptor into lsn
1097
// ELSE Copy lsn parameter into lsn
1098

1099        LogLSN lsn;
1100        boolean truncateHead = false;
1101
1102        if( truncLSN.equals(LogLSN.HEAD_LSN) ||
1103            truncLSN.equals(logControlDescriptor.headLSN) ) {
1104            lsn = new LogLSN(logControlDescriptor.headLSN);
1105            truncateHead = true;
1106        } else if( truncLSN.equals(LogLSN.TAIL_LSN) )
1107            lsn = new LogLSN(logControlDescriptor.tailLSN);
1108        else
1109            lsn = new LogLSN(truncLSN);
1110
1111        // Check the lsn parameter to ensure it is within the range of log records
1112
// IF lsn < log tail LSN (in Log_FileDescriptor)
1113
// Unlock the Log_FileDescriptor latch
1114
// Return LOG_NEW_TAIL_TOO_LOW
1115
// ELSE
1116
// IF lsn > log head LSN
1117
// Unlock the Log_FileDescriptor latch
1118
// Return LOG_NEW_TAIL_TOO_HIGH
1119

1120        if( lsn.lessThan(logControlDescriptor.tailLSN) )
1121            throw new LogException(null,LogException.LOG_NEW_TAIL_TOO_LOW,7);
1122        else if( lsn.greaterThan(logControlDescriptor.headLSN) )
1123            throw new LogException(null,LogException.LOG_NEW_TAIL_TOO_HIGH,8);
1124
1125        // IF log head is being truncated &&
1126
// inclusive parameter = LOG_TAIL_NOT_INCLUSIVE
1127
// Set Truncation record to the lsn specified (head LSN)
1128
// and the New Tail LSN to the next lsn;
1129
// ELSE
1130
// set truncation record and new log tail LSN depending
1131
// on whether or not LOG_TAIL_INCLUSIVE was set. Either way the
1132
// record pointed to by the current lsn must be read first
1133

1134        LogLSN truncationRecord;
1135        LogLSN newTailRecord;
1136        boolean truncLastExtent = false;
1137
1138        if( truncateHead &&
1139            inclusive == TAIL_NOT_INCLUSIVE ) {
1140            truncationRecord = new LogLSN(lsn);
1141            newTailRecord = new LogLSN(logControlDescriptor.nextLSN);
1142        } else {
1143
1144            // IF inclusive parameter = LOG_TAIL_INCLUSIVE and
1145
// lsn parameter = log tail LSN (in Log_FileDescriptor)
1146
// (then there is nothing to truncate)
1147
// Unlock the Log_FileDescriptor latch
1148
// Return LOG_SUCCESS
1149

1150            if( inclusive == TAIL_INCLUSIVE &&
1151                lsn.equals(logControlDescriptor.tailLSN) ) {
1152                return;
1153            }
1154
1155            // Call Log_PositionFilePointer to position file pointer at the
1156
// start of the record specified by the lsn parameter
1157
// Allow any error to pass to the caller.
1158

1159            LogExtent logEDP = positionFilePointer(lsn,0,LogExtent.ACCESSTYPE_READ);
1160
1161            // Issue READ for the log record header
1162
// IF not successful return LOG_READ_ERROR
1163

1164            byte[] headerBytes = new byte[LogRecordHeader.SIZEOF];
1165            int bytesRead = 0;
1166
1167            try {
1168                bytesRead = logEDP.fileHandle.fileRead(headerBytes);
1169            } catch( LogException le ) {
1170                logEDP.lastAccess = LogExtent.ACCESSTYPE_UNKNOWN;
1171                throw new LogException(null,LogException.LOG_READ_FAILURE,11);
1172            }
1173
1174            logEDP.cursorPosition += bytesRead;
1175            LogRecordHeader recordHeader = new LogRecordHeader(headerBytes,0);
1176
1177            // Check that retrieved record is not an extent link record
1178
// IF it is
1179
// Unlock the Log_FileDescriptor latch
1180
// Return LOG_INVALID_TAIL
1181

1182            if( recordHeader.recordType == LINK )
1183                throw new LogException(null,LogException.LOG_INVALID_TAIL,12);
1184
1185            // Now set truncation record, and new tail LSN according to whether
1186
// or not LOG_TAIL_INCLUSIVE was specified
1187

1188            if( inclusive == TAIL_INCLUSIVE ) {
1189                // The specified LSN is to be retained in the logfile so
1190
// set the truncation record to the previous LSN and the
1191
// new tail to the specified LSN
1192

1193                truncationRecord = new LogLSN(recordHeader.previousLSN);
1194                newTailRecord = new LogLSN(lsn);
1195
1196                // IF the current LSN is the first record in an extent file
1197
// Remember that previous extent file is to be truncated
1198

1199                if( lsn.offset == 0 )
1200                    truncLastExtent = true;
1201            } else {
1202
1203                // The specified LSN is to be truncated from the logfile so
1204
// set the truncation record to the specified LSN and the
1205
// new tail to the next LSN
1206

1207                truncationRecord = new LogLSN(lsn);
1208                newTailRecord = new LogLSN(recordHeader.nextLSN);
1209            }
1210        }
1211
1212        // Now that the true truncation point in the log file is known, work out
1213
// how many extent files (if any) can be unlinked
1214
// - Set first_extent to extent number from log tail LSN
1215
// - Set last_extent to extent number from truncation point LSN
1216

1217        int firstExtent = logControlDescriptor.tailLSN.extent;
1218        int lastExtent = truncationRecord.extent;
1219
1220        // IF log head is being truncated &&
1221
// inclusive parameter = LOG_TAIL_NOT_INCLUSIVE
1222
// Set log tail LSN to current log head LSN
1223
// Set log head LSN in Log_ControlDescriptor structure to LOG_NULL_LSN
1224
// ELSE
1225
// Set log tail LSN in Log_ControlDescriptor structure
1226
// to truncation point LSN
1227

1228        if( truncateHead &&
1229            inclusive == TAIL_NOT_INCLUSIVE ) {
1230            logControlDescriptor.tailLSN.copy(newTailRecord);
1231            logControlDescriptor.headLSN.copy(LogLSN.NULL_LSN);
1232        } else
1233            logControlDescriptor.tailLSN.copy(newTailRecord);
1234
1235        // Write (and implicitly sync) the Log_ControlDescriptor structure
1236
// to the control file. Allow any error to pass to the caller.
1237

1238        writeControlFile();
1239
1240        // Now unlink any extent files no longer required
1241
// This involves processing each of the extent files in the range
1242
// FirstExtent to LastExtent-1.
1243
// Note: If the TruncationRecord is a link record (last in the extent
1244
// file), then the LastExtent must also be processed.
1245

1246        if( truncLastExtent )
1247            lastExtent++;
1248
1249        for( int extent = firstExtent; extent <= lastExtent-1; extent++ ) {
1250
1251            // IF extent is currently open
1252
// Issue CLOSE for extent file
1253
// IF not successful allow the error to pass to the caller.
1254

1255            LogExtent logEDP = (LogExtent)extentTable.get(new Integer JavaDoc(extent));
1256            if( logEDP != null )
1257                logEDP.fileHandle.fileClose();
1258
1259            // Issue UNLINK for extent file
1260
// IF not successful
1261
// Return LOG_CLOSE_FAILURE
1262

1263            //Start IASRI 4720539
1264
//if( !logEDP.file.delete() )
1265
final LogExtent tmplogEDP = logEDP;
1266            Boolean JavaDoc isdeleted = (Boolean JavaDoc) java.security.AccessController.doPrivileged(
1267        new java.security.PrivilegedAction JavaDoc() {
1268            public Object JavaDoc run(){
1269                        return new Boolean JavaDoc(tmplogEDP.file.delete());
1270                    }
1271                }
1272            );
1273            if(!isdeleted.booleanValue())
1274                throw new LogException(null,LogException.LOG_CLOSE_FAILURE,15);
1275            //End IASRI 4720539
1276
// Unchain the Log_ExtentDescriptor block, set its BlockValid
1277
// field to binary zeroes and deallocate it.
1278

1279            extentTable.remove(new Integer JavaDoc(extent));
1280            logEDP.finalize();
1281        }
1282
1283        // If the cushion file does not exist and at least one extents has
1284
// just been removed, now is a good time to try and restore the
1285
// cushion file.
1286
// Call RestoreCushion but specify that the upcall should not
1287
// be called if the restore fails, as it should already have
1288
// been called when the cushion was first freed.
1289

1290        if( !cushionExists &&
1291            firstExtent <= lastExtent - 1 )
1292            restoreCushion(false);
1293
1294        // Call the platform specific SUPOS_LOG_FREE_FILE_STORAGE macro to
1295
// release any unwanted areas of the extent file containing the TAIL LSN
1296
// Allow any error to pass to the caller.
1297

1298        if( logControlDescriptor.tailLSN.offset > 0 )
1299            freeFileStorage(logControlDescriptor.tailLSN);
1300
1301        // If the log head has been set to 00000000.00000000, then ensure that
1302
// the next record which is written to the log causes the log control
1303
// data to be forced. Otherwise, should a crash occur AFTER writing the
1304
// record and BEFORE writing the control file, when the log is re-opened
1305
// during restart, it will be assumed that the log is empty and no
1306
// scanning for records 'beyond the end of the log' will take place.
1307

1308        if( logControlDescriptor.headLSN.isNULL() )
1309            recordsWritten = CONTROL_FORCE_INTERVAL;
1310
1311    }
1312
1313    /**Ensures that the log is written up to the given point.
1314     *
1315     * @param chkLSN The last LSN which must be written.
1316     *
1317     * @return
1318     *
1319     * @exception LogException The operation failed.
1320     *
1321     * @see
1322     */

1323    synchronized void checkLSN( LogLSN chkLSN )
1324        throws LogException {
1325
1326        // Check BlockValid field in Log_FileDescriptor block pointed to
1327
// by logHandle parameter, and ensure it is valid
1328
// IF not valid Log_FileDescriptor
1329
// Return LOG_INVALID_FILE_DESCRIPTOR
1330

1331        if( blockValid != this )
1332            throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);
1333
1334        // IF not LogInitialised
1335
// Return LOG_NOT_INITIALISED
1336

1337        if( !logControl.logInitialised )
1338            throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);
1339
1340        // IF ReadOnly log
1341
// Return LOG_READ_ONLY_ACCESS
1342

1343        if( logControl.logReadOnly )
1344            throw new LogException(null,LogException.LOG_READ_ONLY_ACCESS,3);
1345
1346        // IF the lsn parameter is equal to LOG_HEAD_LSN
1347
// Copy head LSN from Log_FileDescriptor into lsn
1348
// ELSE IF the lsn parameter is equal to LOG_TAIL_LSN
1349
// Copy tail LSN from Log_FileDescriptor into lsn
1350
// ELSE Copy lsn parameter into lsn
1351

1352        LogLSN lsn;
1353
1354        if( chkLSN.equals(LogLSN.HEAD_LSN) )
1355            lsn = new LogLSN(logControlDescriptor.headLSN);
1356        else if( chkLSN.equals(LogLSN.TAIL_LSN) )
1357            lsn = new LogLSN(logControlDescriptor.tailLSN);
1358        else
1359            lsn = new LogLSN(chkLSN);
1360
1361        // IF lsn value is less than log tail LSN
1362
// Return LOG_SUCCESS
1363

1364        if( lsn.lessThan(logControlDescriptor.tailLSN) ) {
1365            return;
1366        }
1367
1368        // IF log file is empty (log head = LOG_NULL_LSN)
1369
// Unlock the Log_FileDescriptor latch
1370
// Return LOG_SUCCESS
1371

1372        if( logControlDescriptor.headLSN.isNULL() ) {
1373            return;
1374        }
1375
1376        // IF lsn value is greater than log head LSN
1377
// Copy head LSN from Log_FileDescriptor into lsn parameter
1378

1379        if( lsn.greaterThan(logControlDescriptor.headLSN) )
1380            lsn.copy(logControlDescriptor.headLSN);
1381
1382        // Determine the extent which contains the record to be forced (this is
1383
// derived from the 'extent' part of the lsn parameter) - remember this
1384
// as LAST_EXTENT
1385

1386        int lastExtent = lsn.extent;
1387
1388        // Determine the extent which contains the log tail LSN, remember this
1389
// as FIRST_EXTENT
1390

1391        int firstExtent = logControlDescriptor.tailLSN.extent;
1392
1393        // Now force each of the extent files (FIRST_EXTENT to LAST_EXTENT
1394
// inclusive)
1395

1396        for( int extent = firstExtent; extent <= lastExtent; extent++ ) {
1397
1398            // IF extent is currently open (Log_ExtentDescriptor block exists)
1399
// IF the Written flag in the Log_ExtentDescriptor is TRUE
1400
// Issue FSYNC for extent file
1401
// IF not successful allow the error to pass to the caller.
1402
// ELSE
1403
// Set 'extent written' flag to FALSE
1404

1405            LogExtent logEDP = (LogExtent)extentTable.get(new Integer JavaDoc(extent));
1406            if( logEDP != null &&
1407                logEDP.writtenSinceLastForce ) {
1408                logEDP.fileHandle.fileSync();
1409                logEDP.writtenSinceLastForce = false;
1410            }
1411        }
1412
1413        // IF 'extent' part of head LSN is same as LAST_EXTENT
1414
// Force the Log_ControlDescriptor structure to the control file
1415
// by issuing WRITE (implied sync)
1416
// IF not successful allow the error to pass to the caller.
1417
// ELSE
1418
// Don't force control data, since we do not want control data to
1419
// be 'ahead' of extent data
1420
// The following block of code will no longer be executed as a result
1421
// of a performance suggestion. This reduces the number of occasions
1422
// when the control data is forced to disk
1423

1424        /*
1425          if( logControlDescriptor.headLSN.extent == lastExtent )
1426          writeControlFile();
1427        */

1428
1429    }
1430
1431    /**Opens a cursor on the log file.
1432     *
1433     * @param startLSN The start of the browse.
1434     * @param endLSN The end of the browse.
1435     *
1436     * @return The new cursor.
1437     *
1438     * @exception LogException The browse was not possible.
1439     *
1440     * @see
1441     */

1442    synchronized LogCursor openCursor( LogLSN startLSN,
1443                                       LogLSN endLSN )
1444        throws LogException {
1445
1446        // Check BlockValid field in Log_FileDescriptor block and
1447
// ensure it is valid
1448
// IF not valid Log_FileDescriptor
1449
// Return LOG_INVALID_FILE_DESCRIPTOR
1450

1451        if( blockValid != this )
1452            throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);
1453
1454        // IF not LogInitialised
1455
// Return LOG_NOT_INITIALISED
1456

1457        if( !logControl.logInitialised )
1458            throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);
1459
1460        // Allocate a Log_CursorDescriptor block
1461
// IF allocate fails
1462
// Return LOG_INSUFFICIENT_MEMORY
1463

1464        LogCursor cursor = new LogCursor(logControl,this,startLSN,endLSN);
1465        if( cursor == null ) {
1466            throw new LogException(null,LogException.LOG_INSUFFICIENT_MEMORY,4);
1467        }
1468        
1469
1470        // Add the Log_CursorDescriptor block to the chain of similar blocks
1471
// hung off the LogFileDescriptor (anchor is CursorDescriptorHead)
1472

1473        cursors.add(cursor);
1474
1475        return cursor;
1476    }
1477
1478    /**Closes the cursor.
1479     *
1480     * @param LogCursor The cursor to be closed.
1481     *
1482     * @return
1483     *
1484     * @exception LogException The cursor could not be closed.
1485     *
1486     * @see
1487     */

1488    synchronized void closeCursor( LogCursor cursor )
1489        throws LogException {
1490
1491        // Check BlockValid field in Log_CursorDescriptor block and
1492
// ensure it is valid
1493
// IF not valid Log_CursorDescriptor
1494
// Return LOG_INVALID_CURSOR
1495

1496        if( cursor == null || cursor.blockValid != cursor )
1497            throw new LogException(null,LogException.LOG_INVALID_CURSOR,1);
1498
1499        // Check BlockValid field in Log_FileDescriptor block pointed to
1500
// by field in Log_CursorDescriptor block and ensure it is valid
1501

1502        if( blockValid != this )
1503            throw new LogException(null,LogException.LOG_INVALID_CURSOR,2);
1504
1505        // IF not LogInitialised
1506
// Return LOG_NOT_INITIALISED
1507

1508        if( !logControl.logInitialised )
1509            throw new LogException(null,LogException.LOG_NOT_INITIALISED,3);
1510
1511        // Now we know the blocks are valid.
1512
// Remove the Log_CursorDescriptor block from the chain hung off
1513
// the Log_FileDescriptor (CursorDescriptorHead)
1514

1515        cursors.remove(cursor);
1516
1517        // Deallocate the Log_CursorDescriptor block
1518

1519        cursor.finalize();
1520
1521    }
1522
1523    /**Positions the file pointer to the given position in the log.
1524     * This internal method does not need to be synchronized.
1525     *
1526     * @param lsn The base position sought.
1527     * @param extra An extra offset for the position.
1528     * @param accessType The type of access.
1529     *
1530     * @return The extent containing the given position.
1531     *
1532     * @exception LogException The operation failed.
1533     *
1534     * @see
1535     */

1536    LogExtent positionFilePointer( LogLSN currentLSN,
1537                                   int extra,
1538                                   int accessType )
1539        throws LogException {
1540
1541        boolean extentJustOpened = false; // Remember open operation
1542

1543        // Run the extent chain to see if extent file is already open
1544

1545        LogExtent extent = (LogExtent)extentTable.get(new Integer JavaDoc(currentLSN.extent));
1546
1547        // Open the extent file if it was not found in the extent chain
1548

1549        if( extent == null ) {
1550
1551            // Open the new extent file
1552

1553            extent = openExtent(currentLSN.extent);
1554            extentJustOpened = true;
1555        }
1556
1557        // If the current cursor position for the extent is not in the
1558
// required position, seek to the correct position
1559

1560        if( extent.cursorPosition != currentLSN.offset + extra ||
1561            extent.lastAccess != accessType ) {
1562
1563            // lseek to the correct offset in the open extent file
1564
// if the last cursor position is unknown or the distance
1565
// of the seek from the start of the file is closer to the
1566
// required position than the current position do a seek
1567
// from the start of the file, otherwise do a seek from the
1568
// current position.
1569

1570            int seekDist = ((currentLSN.offset + extra) > extent.cursorPosition) ?
1571                (currentLSN.offset + extra - extent.cursorPosition) :
1572                (extent.cursorPosition - currentLSN.offset - extra);
1573
1574            try {
1575                if( extent.lastAccess == LogExtent.ACCESSTYPE_UNKNOWN ||
1576                    currentLSN.offset + extra < seekDist )
1577                    extent.fileHandle.fileSeek(currentLSN.offset+extra,LogFileHandle.SEEK_ABSOLUTE);
1578                else
1579                    extent.fileHandle.fileSeek(currentLSN.offset+extra-extent.cursorPosition,LogFileHandle.SEEK_RELATIVE);
1580            } catch( LogException le ) {
1581                if( extentJustOpened ) {
1582                    extentTable.remove(new Integer JavaDoc(currentLSN.extent));
1583                    extent.finalize();
1584                }
1585
1586                throw new LogException(null,LogException.LOG_READ_FAILURE,3);
1587            }
1588
1589            extent.cursorPosition = currentLSN.offset + extra;
1590            extent.lastAccess = accessType;
1591        }
1592
1593        // Return the file descriptor for the extent file
1594

1595
1596        return extent;
1597    }
1598
1599    /**Frees the cushion file.
1600     * <p>
1601     * This internal method does not need to be synchronized.
1602     *
1603     * @param
1604     *
1605     * @return
1606     *
1607     * @see
1608     */

1609    private void freeCushion() {
1610        // If the cushion file exists, remove it.
1611

1612        if( cushionExists ) {
1613
1614            // Delete the cushion file.
1615
// Start IASRI 4720539
1616
//logControl.cushionFile.delete();
1617
java.security.AccessController.doPrivileged(
1618                new java.security.PrivilegedAction JavaDoc() {
1619                    public Object JavaDoc run(){
1620                        logControl.cushionFile.delete();
1621                        return null;
1622                    }
1623                }
1624            );
1625            // End IASRI 4720539
1626
cushionExists = false;
1627        }
1628
1629    }
1630
1631    /**Restores the cushion file
1632     * <p>
1633     * This internal method does not need to be synchronized.
1634     *
1635     * @param callUpcall Indicates whether the upcall should be called.
1636     *
1637     * @return
1638     *
1639     * @exception LogException The operation failed.
1640     *
1641     * @see
1642     */

1643    void restoreCushion( boolean callUpcall )
1644        throws LogException {
1645
1646        // IF the LOG_CUSHION_SIZE > 0
1647

1648        if( CUSHION_SIZE > 0 ) {
1649
1650            // if the cushion file already exists, set the flag in the
1651
// File Descriptor block to say so.
1652

1653            if( !logControl.cushionFile.exists() ) {
1654                LogFileHandle cushionFH; // Cushion file descriptor
1655
int openOptions = LogFileHandle.OPEN_RDWR |
1656                    LogFileHandle.OPEN_CREAT |
1657                    LogFileHandle.OPEN_SYNC;
1658
1659                // Create an empty log file as a storage cushion
1660
// Issue OPEN request for $REGIONDIR/log/cushion
1661
// IF OPEN fails
1662
// Call function whose address is stored in UpcallFunction,
1663
// passing a reason value of LOG_CALLBACK_REASON_SOS
1664
// Unlock the Log_ProcessSharedLock
1665
// Return
1666

1667                try {
1668                    cushionFH = new LogFileHandle(logControl.cushionFile,openOptions);
1669                } catch( LogException le ) {
1670                    if( callUpcall && !upcallInProgress ) {
1671                        upcallInProgress = true;
1672                        upcallTarget.upcall(CALLBACK_REASON_SOS);
1673                        upcallInProgress = false;
1674                    }
1675                    throw new LogException(null,LogException.LOG_OPEN_FAILURE,3);
1676                }
1677
1678                // Use Log_AllocFileStorage to create a file the
1679
// size LOG_CUSHION_SIZE
1680
// IF Log_AllocFileStorage fails
1681
// Call function whose address is stored in UpcallFunction,
1682
// passing a reason value of LOG_CALLBACK_REASON_SOS
1683
// CLOSE & Unlink the cushion file
1684
// Unlock the Log_ProcessSharedLock
1685
// Return
1686

1687                try {
1688                    cushionFH.allocFileStorage(CUSHION_SIZE);
1689                } catch( LogException le ) {
1690                    cushionFH.finalize();
1691                    // Start IASRI 4720539
1692
//logControl.cushionFile.delete();
1693
java.security.AccessController.doPrivileged(
1694                        new java.security.PrivilegedAction JavaDoc() {
1695                            public Object JavaDoc run(){
1696                                logControl.cushionFile.delete();
1697                                return null;
1698                            }
1699                        }
1700                    );
1701                    // End IASRI 4720539
1702

1703                    if( callUpcall && !upcallInProgress ) {
1704                        upcallInProgress = true;
1705                        upcallTarget.upcall(CALLBACK_REASON_SOS);
1706                        upcallInProgress = false;
1707                    }
1708                    cushionExists = false;
1709                    throw new LogException(null,LogException.LOG_OPEN_FAILURE,4);
1710                }
1711
1712                // CLOSE the cushion file
1713

1714                cushionFH.finalize();
1715            }
1716
1717            cushionExists = true;
1718        }
1719
1720    }
1721
1722    /**Writes the control file.
1723     * This internal method does not need to be synchronized.
1724     *
1725     * @param
1726     *
1727     * @return
1728     *
1729     * @exception LogException The write failed.
1730     *
1731     * @see
1732     */

1733    void writeControlFile() throws LogException {
1734
1735        // BUGFIX (Ram J) This fixes the log corruption problem.
1736
// The log extents have to be forced every time control
1737
// information is written. If not, there is a chance that
1738
// the control information (particularly headLSN) will go
1739
// inconsistent. The extent log that the headLSN in controlFile
1740
// points to, may not exist in the extent log, if it is
1741
// not forced. So, if JTS crashes, during recovery/reconstruction
1742
// there will be no log in the extents corresponding to the
1743
// headLSN stored in the control file. The fix is to force
1744
// all the dirty extents, everytime the control information
1745
// is updated.
1746
Enumeration extents = extentTable.elements();
1747        while (extents.hasMoreElements()) {
1748            LogExtent nextEDP = (LogExtent) extents.nextElement();
1749            if (nextEDP.writtenSinceLastForce) {
1750                try {
1751                    nextEDP.fileHandle.fileSync();
1752                    nextEDP.writtenSinceLastForce = false;
1753                } catch (LogException le) {
1754                    throw new LogException(null,
1755                                           LogException.LOG_ERROR_FORCING_LOG,
1756                                           14);
1757                }
1758            }
1759        }
1760
1761        // Move the file pointer to the beginning of the control file
1762

1763        logFileHandle.fileSeek(0,LogFileHandle.SEEK_ABSOLUTE);
1764
1765        // Write out the control data to the control file
1766

1767        byte[] controlBytes = new byte[LogControlDescriptor.SIZEOF];
1768        logControlDescriptor.toBytes(controlBytes,0);
1769        int bytesWritten = logFileHandle.fileWrite(controlBytes);
1770
1771    }
1772
1773    /**Opens the given extent.
1774     * <p>
1775     * This internal method does not need to be synchronized.
1776     *
1777     * @param extent The extent to open.
1778     *
1779     * @return The extent opened.
1780     *
1781     * @exception LogException The open failed.
1782     *
1783     * @see
1784     */

1785    LogExtent openExtent( int extent ) throws LogException {
1786
1787        // Build the extent file name from the CurrentLSN parameter
1788

1789        File extentFile = logControl.extentFile(logFileName,LogExtent.modExtent(extent));
1790
1791        // Issue an OPEN request for the file
1792
// IF not successful (rc == -1 and not EOF)
1793
// Return LOG_OPEN_FAILURE
1794

1795        int openOptions = LogFileHandle.OPEN_RDWR | LogFileHandle.OPEN_CREAT;
1796        if( logControl.logReadOnly )
1797            openOptions = LogFileHandle.OPEN_RDONLY;
1798
1799        LogFileHandle extentFH = new LogFileHandle(extentFile,openOptions);
1800
1801        // Allocate a Log_ExtentDescriptor block and initialise it
1802

1803        LogExtent logEDP = new LogExtent(extent,extentFH,extentFile);
1804        if( logEDP == null ) {
1805            extentFH.finalize();
1806            throw new LogException(null,LogException.LOG_INSUFFICIENT_MEMORY,2);
1807        }
1808
1809        // Use the already hashed extent number to find the position in the
1810
// hash table and add it to the chain
1811

1812        extentTable.put(new Integer JavaDoc(extent),logEDP);
1813        logEDP.blockValid = logEDP;
1814
1815        return logEDP;
1816    }
1817
1818    /**Frees file storage for the file.
1819     * This internal method does not need to be synchronized.
1820     *
1821     * @param tailLSN The point from which storage is not required.
1822     *
1823     * @return
1824     *
1825     * @exception LogException The operation failed.
1826     *
1827     * @see
1828     */

1829    void freeFileStorage( LogLSN tailLSN )
1830        throws LogException {
1831
1832        // Using the extent containing the tail LSN, calculate the number of
1833
// bytes up to but not including the log tail record
1834
// Zero this space by issuing an FCLEAR request for the extent file
1835
// IF not successful
1836
// Unlock the Log_FileDescriptor mutex
1837
// Return LOG_WRITE_FAILURE
1838

1839        int bytesToClear = tailLSN.offset;
1840        if( bytesToClear == 0 ) {
1841            return;
1842        }
1843
1844        // Build LSN which will cause Log_PositionFilePointer to
1845
// position the file pointer at the start of the extent file
1846

1847        LogLSN startOfExtent = new LogLSN(tailLSN.extent,0);
1848        LogExtent logEDP = positionFilePointer(startOfExtent,0,LogExtent.ACCESSTYPE_UNKNOWN);
1849
1850        // Write the change to permanent storage via an FSYNC request
1851

1852        logEDP.fileHandle.fileSync();
1853
1854    }
1855
1856    /**Checks restart record information.
1857     * This internal method does not need to be synchronized.
1858     *
1859     * @param fileHandle The handle of the file.
1860     * @param restartNumber The restart number.
1861     * @param restartInfo An array which will contain the length and timestamp.
1862     *
1863     * @return
1864     *
1865     * @exception LogException The operation failed.
1866     *
1867     * @see
1868     */

1869    static void checkRestart( LogFileHandle fileHandle,
1870                              int restartNumber,
1871                              int[/*2*/] restartInfo )
1872        throws LogException {
1873
1874        // Initialise callers output parameters
1875

1876        restartInfo[0] = 0; // length
1877
restartInfo[1] = 0; // time stamp
1878

1879        // Calculate the offsets within control file for both restart records
1880
// Use LSEEK to move to the first record and issue a READ for its
1881
// Log_RestartDescriptor block
1882

1883        byte[] restartBytes = new byte[LogRestartDescriptor.SIZEOF];
1884
1885        int offset = restartPosition(restartNumber);
1886        fileHandle.fileSeek(offset,LogFileHandle.SEEK_ABSOLUTE);
1887        int bytesRead = fileHandle.fileRead(restartBytes);
1888        LogRestartDescriptor logRD = new LogRestartDescriptor(restartBytes,0);
1889
1890        // IF the READ is successful and it return the restart data
1891

1892        if( bytesRead > 0 ) {
1893
1894            // Check that the RestartValid value in the
1895
// Log_RestartDescriptor block matches the record offset
1896
// IF it matches
1897
// Use LSEEK to move to the end of the restart data and read
1898
// in the matching Log_RestartDescriptor block
1899

1900            if( logRD.restartValid == restartPosition(restartNumber) ) {
1901                fileHandle.fileSeek(logRD.restartDataLength,
1902                                    LogFileHandle.SEEK_RELATIVE);
1903                bytesRead = fileHandle.fileRead(restartBytes);
1904                LogRestartDescriptor logRDEnd = new LogRestartDescriptor(restartBytes,0);
1905
1906
1907                // Check that the two Log_RestartDescriptor blocks are the same
1908
// IF they are identical
1909
// Assume the first version of restart data is valid
1910
// Remember the timestamp contained in the block
1911

1912                if( logRD.equals(logRDEnd) ) {
1913                    restartInfo[0] = logRD.restartDataLength;
1914                    restartInfo[1] = logRD.timeStamp;
1915                }
1916                else
1917                    throw new LogException(null,LogException.LOG_CORRUPTED,1);
1918            }
1919        }
1920
1921    }
1922
1923    /**Dumps the state of the object.
1924     *
1925     * @param
1926     *
1927     * @return
1928     *
1929     * @exception LogException The operation failed.
1930     *
1931     * @see
1932     */

1933    void dump() throws LogException {
1934        LogExtent logEDP; // Extent file descriptor
1935
LogCursor logCuDP; // ptr to cursor descriptor
1936

1937        // Check that the LogHandle passed points to a genuine File
1938
// Descriptor block using the BlockValid field.
1939
// IF the block is not genuine
1940
// Return LOG_INVALID_FILE_DESCRIPTOR
1941

1942        if( blockValid != this )
1943            throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);
1944
1945        // LOOP for each of the elements in the log file's extent hash table
1946

1947        Enumeration extents = extentTable.elements();
1948        while( extents.hasMoreElements() ) {
1949            logEDP = (LogExtent)extents.nextElement();
1950        }
1951
1952        java.util.Iterator JavaDoc curs = cursors.iterator();
1953        while( curs.hasNext() ) {
1954            logCuDP = (LogCursor)curs.next();
1955        }
1956
1957    }
1958
1959    /**Removes all extent information from the log file.
1960     * This internal method does not need to be synchronized.
1961     *
1962     * @param
1963     *
1964     * @return
1965     *
1966     * @see
1967     */

1968    void cleanUpExtents() {
1969
1970        Enumeration extents = extentTable.elements();
1971        while( extents.hasMoreElements() ) {
1972            LogExtent logEDP = (LogExtent)extents.nextElement();
1973            extentTable.remove(new Integer JavaDoc(logEDP.extentNumber));
1974            logEDP.finalize();
1975        }
1976        extentTable = null;
1977
1978    }
1979
1980    /**Returns the alternate restart number.
1981     *
1982     * @param restart The current restart number.
1983     *
1984     * @return The new restart number.
1985     *
1986     * @see
1987     */

1988
1989    final static int alternateRestart( int restart) {
1990        return (restart == 1) ? 2 : 1;
1991    }
1992
1993    /**Returns the offset of the specified restart number.
1994     *
1995     * @param restart The restart number.
1996     *
1997     * @return The restart position.
1998     *
1999     * @see
2000     */

2001    final static int restartPosition( int restart ) {
2002        return (restart == 1) ? RESTART_OFFSET_1 : RESTART_OFFSET_2;
2003    }
2004
2005    /**Returns the log file name.
2006     *
2007     * @param
2008     *
2009     * @return The log file name.
2010     *
2011     * @see
2012     */

2013    final String JavaDoc logFileName() {
2014        return logFileName;
2015    }
2016}
2017
Popular Tags