You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by mi...@apache.org on 2006/04/17 22:15:50 UTC
svn commit: r394767 -
/db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/store/raw/data/CachedPage.java
Author: mikem
Date: Mon Apr 17 13:15:47 2006
New Revision: 394767
URL: http://svn.apache.org/viewcvs?rev=394767&view=rev
Log:
DERBY-758
Back porting fix (357083) from trunk to 10.1 branch for 10.1.3 release.
fixing I/O retry logic to throw an I/O exception rather
than loop forever on I/O read errors. 4 retry's are attempted before
giving up.
Modified:
db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/store/raw/data/CachedPage.java
Modified: db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/store/raw/data/CachedPage.java
URL: http://svn.apache.org/viewcvs/db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/store/raw/data/CachedPage.java?rev=394767&r1=394766&r2=394767&view=diff
==============================================================================
--- db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/store/raw/data/CachedPage.java (original)
+++ db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/store/raw/data/CachedPage.java Mon Apr 17 13:15:47 2006
@@ -49,14 +49,14 @@
Since there are multiple page formats, use this abstract class to implement
cacheable interface.
-
*/
public abstract class CachedPage extends BasePage implements Cacheable
{
- protected boolean alreadyReadPage; // set to true when the page was read by another class
+ protected boolean alreadyReadPage; // true when page read by another
+ // class
- protected byte[] pageData; // the actual page data - this is
+ protected byte[] pageData; // the actual page data - this is
// the 'buffer' in the buffer cache
// The isDirty flag indicates if the pageData or pageHeader has been
@@ -70,17 +70,18 @@
// is to stop the cache cleaning from skipping over this (latched) page
// even though it has not really been modified yet.
- protected boolean isDirty; // must be set to true
- // whenever the pageData array is touched directly
- // or indirectly.
+ protected boolean isDirty; // must be set to true whenever the
+ // pageData array is touched
+ // directly or indirectly.
protected boolean preDirty; // set to true if the page is clean
- // and the pageData array is about to be
- // touched directly or indirectly.
+ // and the pageData array is about
+ // to be touched directly or
+ // indirectly.
- protected int initialRowCount; // keep a running count of rows for
- // estimated row count.
+ protected int initialRowCount; // keep a running count of rows for
+ // estimated row count.
private long containerRowCount; // the number of rows in the
// container when this page is read
@@ -129,10 +130,11 @@
super();
}
- public final void setFactory(BaseDataFileFactory factory) {
- dataFactory = factory;
- pageCache = factory.getPageCache();
- containerCache = factory.getContainerCache();
+ public final void setFactory(BaseDataFileFactory factory)
+ {
+ dataFactory = factory;
+ pageCache = factory.getPageCache();
+ containerCache = factory.getContainerCache();
}
/**
@@ -144,29 +146,34 @@
protected void initialize()
{
super.initialize();
- isDirty = false;
- preDirty = false;
- initialRowCount = 0;
- containerRowCount = 0;
+ isDirty = false;
+ preDirty = false;
+ initialRowCount = 0;
+ containerRowCount = 0;
}
/*
** Methods of Cacheable
*/
- /**
- Find the container and then read the page from that container.
-
- @return always true, higher levels have already checked the page number is
- valid for an open.
-
- @exception StandardException Standard Cloudscape policy.
-
- @see Cacheable#setIdentity
- */
- public Cacheable setIdentity(Object key) throws StandardException {
-
- if (SanityManager.DEBUG) {
+ /**
+ * Find the container and then read the page from that container.
+ * <p>
+ * This is the way new pages enter the page cache.
+ * <p>
+ *
+ * @return always true, higher levels have already checked the page number
+ * is valid for an open.
+ *
+ * @exception StandardException Standard Cloudscape policy.
+ *
+ * @see Cacheable#setIdentity
+ **/
+ public Cacheable setIdentity(Object key)
+ throws StandardException
+ {
+ if (SanityManager.DEBUG)
+ {
SanityManager.ASSERT(key instanceof PageKey);
}
@@ -175,15 +182,22 @@
PageKey newIdentity = (PageKey) key;
FileContainer myContainer =
- (FileContainer) containerCache.find(newIdentity.getContainerId());
+ (FileContainer) containerCache.find(newIdentity.getContainerId());
+
setContainerRowCount(myContainer.getEstimatedRowCount(0));
try
{
if (!alreadyReadPage)
- readPage(myContainer, newIdentity); // read in the pageData array from disk
+ {
+ // Fill in the pageData array by reading bytes from disk.
+ readPage(myContainer, newIdentity);
+ }
else
+ {
+ // pageData array already filled
alreadyReadPage = false;
+ }
// if the formatID on disk is not the same as this page instance's
// format id, instantiate the real page object
@@ -192,7 +206,8 @@
int onPageFormatId = FormatIdUtil.readFormatIdInteger(pageData);
if (fmtId != onPageFormatId)
{
- return changeInstanceTo(onPageFormatId, newIdentity).setIdentity(key);
+ return changeInstanceTo(
+ onPageFormatId, newIdentity).setIdentity(key);
}
// this is the correct page instance
@@ -211,19 +226,33 @@
return this;
}
- /**
- Find the container and then create the page in that container.
-
- @return new page, higher levels have already checked the page number is
- valid for an open.
-
- @exception StandardException Standard Cloudscape policy.
-
- @see Cacheable#createIdentity
- */
- public Cacheable createIdentity(Object key, Object createParameter) throws StandardException {
+ /**
+ * Find the container and then create the page in that container.
+ * <p>
+ * This is the process of creating a new page in a container, in that
+ * case no need to read the page from disk - just need to initialize it
+ * in the cache.
+ * <p>
+ *
+ * @return new page, higher levels have already checked the page number is
+ * valid for an open.
+ *
+ * @param key Which page is this?
+ * @param createParameter details needed to create page like size,
+ * format id, ...
+ *
+ * @exception StandardException Standard exception policy.
+ *
+ * @see Cacheable#createIdentity
+ **/
+ public Cacheable createIdentity(
+ Object key,
+ Object createParameter)
+ throws StandardException
+ {
- if (SanityManager.DEBUG) {
+ if (SanityManager.DEBUG)
+ {
SanityManager.ASSERT(key instanceof PageKey);
}
@@ -239,12 +268,14 @@
SQLState.DATA_UNKNOWN_PAGE_FORMAT, newIdentity);
}
- // createArgs[0] contains the interger form of the formatId
+ // createArgs[0] contains the integer form of the formatId
// if it is not the same as this instance's formatId, instantiate the
// real page object
if (createArgs[0] != getTypeFormatId())
{
- return changeInstanceTo(createArgs[0], newIdentity).createIdentity(key, createParameter);
+ return(
+ changeInstanceTo(createArgs[0], newIdentity).createIdentity(
+ key, createParameter));
}
// this is the correct page instance
@@ -257,7 +288,7 @@
/*
* if we need to grow the container and the page has not been
- * preallocated , writing page before the log is written so that we
+ * preallocated, writing page before the log is written so that we
* know if there is an IO error - like running out of disk space - then
* we don't write out the log record, because if we do, it may fail
* after the log goes to disk and then the database may not be
@@ -277,23 +308,37 @@
{
if (SanityManager.DEBUG_ON(FileContainer.SPACE_TRACE))
{
- String syncFlag = ((createArgs[1] & WRITE_SYNC) != 0) ? "Write_Sync" :
+ String syncFlag =
+ ((createArgs[1] & WRITE_SYNC) != 0) ? "Write_Sync" :
(((createArgs[1] & WRITE_NO_SYNC) != 0) ? "Write_NO_Sync" :
- "No_write");
- SanityManager.DEBUG(FileContainer.SPACE_TRACE,
- "creating new page " + newIdentity +
- " with " + syncFlag);
+ "No_write");
+
+ SanityManager.DEBUG(
+ FileContainer.SPACE_TRACE,
+ "creating new page " + newIdentity + " with " + syncFlag);
}
}
return this;
}
- /*
- * this object is instantiated to the wrong subtype of cachedPage,
- * this routine will create an object with the correct subtype, transfer all
- * pertinent information from this to the new correct object.
- */
+ /**
+ * Convert this page to requested type, as defined by input format id.
+ * <p>
+ * The current cache entry is a different format id than the requested
+ * type, change it. This object is instantiated to the wrong subtype of
+ * cachedPage, this routine will create an object with the correct subtype,
+ * and transfer all pertinent information from this to the new correct
+ * object.
+ * <p>
+ *
+ * @return The new object created with the input fid and transfered info.
+ *
+ * @param fid The format id of the new page.
+ * @param newIdentity The key of the new page.
+ *
+ * @exception StandardException Standard exception policy.
+ **/
private CachedPage changeInstanceTo(int fid, PageKey newIdentity)
throws StandardException
{
@@ -322,15 +367,19 @@
// avoid creating the data buffer if possible, transfer it to the new
// page if this is the first time the page buffer is used, then
// createPage will create the page array with the correct page size
- if (this.pageData != null) {
+ if (this.pageData != null)
+ {
realPage.alreadyReadPage = true;
realPage.usePageBuffer(this.pageData);
}
+ // RESOLVE (12/15/06) - the following code is commented out, but
+ // not sure why.
+
// this page should not be used any more, null out all its content and
// wait for GC to clean it up
- //destroyPage(); // let this subtype have a chance to get rid of stuff
+ //destroyPage();// let this subtype have a chance to get rid of stuff
//this.pageData = null; // this instance no longer own the data array
//this.pageCache = null;
//this.dataFactory = null;
@@ -339,111 +388,143 @@
return realPage;
}
- /**
- Has the page or its header been modified or about to be modified.
- See comment on class header on meaning of isDirty and preDirty bits.
-
- @see Cacheable#isDirty
- */
- public boolean isDirty() {
-
- synchronized (this) {
+ /**
+ * Is the page dirty?
+ * <p>
+ * The isDirty flag indicates if the pageData or pageHeader has been
+ * modified. The preDirty flag indicates that the pageData or the
+ * pageHeader is about to be modified. The reason for these 2 flags
+ * instead of just one is to accomodate checkpoint. After a clean
+ * (latched) page sends a log record to the log stream but before that page
+ * is dirtied by the log operation, a checkpoint could be taken. If so,
+ * then the redoLWM will be after the log record but, without preDirty, the
+ * cache cleaning will not have waited for the change. So the preDirty bit
+ * is to stop the cache cleaning from skipping over this (latched) page
+ * even though it has not really been modified yet.
+ *
+ * @return true if the page is dirty.
+ *
+ * @see Cacheable#isDirty
+ **/
+ public boolean isDirty()
+ {
+ synchronized (this)
+ {
return isDirty || preDirty;
}
}
- /**
- Has the page or its header been modified.
- See comment on class header on meaning of isDirty and preDirty bits.
- */
- public boolean isActuallyDirty() {
-
- synchronized (this) {
+ /**
+ * Has the page or its header been modified.
+ * <p>
+ * See comment on class header on meaning of isDirty and preDirty bits.
+ * <p>
+ *
+ * @return true if changes have actually been made to the page in memory.
+ **/
+ public boolean isActuallyDirty()
+ {
+ synchronized (this)
+ {
return isDirty;
}
-
}
- /**
- The page or its header is about to be modified.
- See comment on class header on meaning of isDirty and preDirty bits.
- */
+ /**
+ * Set state to indicate the page or its header is about to be modified.
+ * <p>
+ * See comment on class header on meaning of isDirty and preDirty bits.
+ **/
public void preDirty()
{
- synchronized (this) {
+ synchronized (this)
+ {
if (!isDirty)
preDirty = true;
}
}
- /**
- Ensure that container row count is updated if it is too out of sync
- */
+ /**
+ * Set state to indicate the page or its header has been modified.
+ * <p>
+ * See comment on class header on meaning of isDirty and preDirty bits.
+ * <p>
+ **/
+ protected void setDirty()
+ {
+ synchronized (this)
+ {
+ isDirty = true;
+ preDirty = false;
+ }
+ }
+
+ /**
+ * exclusive latch on page is being released.
+ * <p>
+ * The only work done in CachedPage is to update the row count on the
+ * container if it is too out of sync.
+ **/
protected void releaseExclusive()
{
- // look at dirty bit without latching
+ // look at dirty bit without latching, the updating of the row
+ // count is just an optimization so does not need the latch.
+ //
// if this page actually has > 1/8 rows of the entire container, then
- // consider updating the row count if it is different
- // Since allocation page has recordCount of zero, the if clause will
- // never be true for an allocation page.
+ // consider updating the row count if it is different.
+ //
+ // No need to special case allocation pages because it has recordCount
+ // of zero, thus the if clause will never be true for an allocation
+ // page.
if (isDirty && !isOverflowPage() &&
(containerRowCount / 8) < recordCount())
{
int currentRowCount = internalNonDeletedRecordCount();
- int delta = currentRowCount-initialRowCount;
- int posDelta = delta > 0 ? delta : (-delta);
+ int delta = currentRowCount-initialRowCount;
+ int posDelta = delta > 0 ? delta : (-delta);
+
if ((containerRowCount/8) < posDelta)
{
- // we are actually doing quite a bit of change,
- // update container row count
+ // This pages delta row count represents a significant change
+ // with respect to current container row count so update
+ // container row count
FileContainer myContainer = null;
try
{
- myContainer = (FileContainer) containerCache.find(identity.getContainerId());
+ myContainer = (FileContainer)
+ containerCache.find(identity.getContainerId());
+
if (myContainer != null)
{
myContainer.updateEstimatedRowCount(delta);
- setContainerRowCount(myContainer.getEstimatedRowCount(0));
+ setContainerRowCount(
+ myContainer.getEstimatedRowCount(0));
+
initialRowCount = currentRowCount;
// since I have the container, might as well update the
// unfilled information
- myContainer.trackUnfilledPage(identity.getPageNumber(),
- unfilled());
+ myContainer.trackUnfilledPage(
+ identity.getPageNumber(), unfilled());
}
}
- catch(StandardException se)
+ catch (StandardException se)
{
- // do nothing
+ // do nothing, not sure what could fail but this update
+ // is just an optimization so no need to throw error.
}
finally
{
if (myContainer != null)
containerCache.release(myContainer);
}
-
}
}
super.releaseExclusive();
}
- protected void setDirty() {
- synchronized (this) {
- isDirty = true;
- preDirty = false;
- }
- }
-
-
- /**
- Write the page to disk.
-
- @exception StandardException Error writing the page,
-
- @see Cacheable#clean
- */
/**
* Write the page to disk.
@@ -456,7 +537,6 @@
* Also someday it would be fine to allow reads of this page
* while the I/O was taking place.
*
- * So first
*
* @return The identifier to be used to open the conglomerate later.
*
@@ -464,23 +544,30 @@
*
* @see Cacheable#clean
**/
- public void clean(boolean remove) throws StandardException {
+ public void clean(boolean remove) throws StandardException
+ {
// must wait for the page to be unlatched
- synchronized (this) {
-
+ synchronized (this)
+ {
if (!isDirty())
return;
// is someone else cleaning it
- while (inClean) {
- try {
+ while (inClean)
+ {
+ try
+ {
wait();
- } catch (InterruptedException ie) {
+ }
+ catch (InterruptedException ie)
+ {
throw StandardException.interrupt(ie);
}
}
+ // page is not "inClean" by other thread at this point.
+
if (!isDirty())
return;
@@ -492,9 +579,11 @@
// (owner != null), and preLatch.
while ((owner != null) && !preLatch)
{
- try {
- wait();
- } catch (InterruptedException ie)
+ try
+ {
+ wait();
+ }
+ catch (InterruptedException ie)
{
inClean = false;
throw StandardException.interrupt(ie);
@@ -504,10 +593,12 @@
// The page is now effectively latched by the cleaner.
// We only want to clean the page if the page is actually dirtied,
// not when it is just pre-dirtied.
- if (!isActuallyDirty()) {
- preDirty = false; // the person who latched it gives up the
- // latch without really dirtying the page
- inClean = false;
+ if (!isActuallyDirty())
+ {
+ // the person who latched it gives up the
+ // latch without really dirtying the page
+ preDirty = false;
+ inClean = false;
notifyAll();
return;
}
@@ -519,45 +610,85 @@
}
catch(StandardException se)
{
+ // If we get an error while trying to write a page, current
+ // recovery system requires that entire DB is shutdown. Then
+ // when system is rebooted we will run redo recovery which
+ // if it does not encounter disk errors will guarantee to recover
+ // to a transaction consistent state. If this write is a
+ // persistent device problem, redo recovery will likely fail
+ // attempting to the same I/O. Mark corrupt will stop all further
+ // writes of data and log by the system.
throw dataFactory.markCorrupt(se);
}
finally
{
- // if there is something wrong in writing out the page, do not leave
- // it inClean state or it will block the next cleaner forever
-
- synchronized (this) {
+ // if there is something wrong in writing out the page,
+ // do not leave it inClean state or it will block the next cleaner
+ // forever
+ synchronized (this)
+ {
inClean = false;
notifyAll();
}
}
}
- public void clearIdentity() {
+ public void clearIdentity()
+ {
alreadyReadPage = false;
super.clearIdentity();
}
- private void readPage(FileContainer myContainer, PageKey newIdentity) throws StandardException
+ /**
+ * read the page from disk into this CachedPage object.
+ * <p>
+ * A page is read in from disk into the pageData array of this object,
+ * and then put in the cache.
+ * <p>
+ *
+ * @param myContainer the container to read the page from.
+ * @param newIdentity indentity (ie. page number) of the page to read
+ *
+ * @exception StandardException Standard exception policy.
+ **/
+ private void readPage(
+ FileContainer myContainer,
+ PageKey newIdentity)
+ throws StandardException
{
int pagesize = myContainer.getPageSize();
- setPageArray(pagesize);
- for(int i=0;;){
- try {
+ // we will reuse the existing page array if it is same size, the
+ // cache does support caching various sized pages.
+ setPageArray(pagesize);
+ for (int io_retry_count = 0;;)
+ {
+ try
+ {
myContainer.readPage(newIdentity.getPageNumber(), pageData);
break;
-
- } catch (IOException ioe) {
- i++;
-
+ }
+ catch (IOException ioe)
+ {
+ io_retry_count++;
- // we try to read four times because there might have been
- // thread interrupts when we tried to read the data.
- if(i>4){
-
+ // Retrying read I/O's has been found to be successful sometimes
+ // in completing the read without having to fail the calling
+ // query, and in some cases avoiding complete db shutdown.
+ // Some situations are:
+ // spurious interrupts being sent to thread by clients.
+ // unreliable hardware like a network mounted file system.
+ //
+ // The only option other than retrying is to fail the I/O
+ // immediately and throwing an error, thus performance cost
+ // not really a consideration.
+ //
+ // The retry max of 4 is arbitrary, but has been enough that
+ // not many read I/O errors have been reported.
+ if (io_retry_count > 4)
+ {
// page cannot be physically read
StandardException se =
@@ -565,18 +696,33 @@
SQLState.FILE_READ_PAGE_EXCEPTION,
ioe, newIdentity, new Integer(pagesize));
- //if we are in rollforward recovery, it is possible that
- //this page actually does not exist on the disk yet because
- //the log record we are proccessing now is actually create page,
- //we will recreate the page if we are in rollforward
- //recovery, so just throw the exception.
- if(dataFactory.getLogFactory().inRFR())
- throw se;
-
- if (SanityManager.DEBUG)
+ if (dataFactory.getLogFactory().inRFR())
{
+ //if in rollforward recovery, it is possible that this
+ //page actually does not exist on the disk yet because
+ //the log record we are proccessing now is actually
+ //creating the page, we will recreate the page if we
+ //are in rollforward recovery, so just throw the
+ //exception.
+ throw se;
+ }
+ else
+ {
+ if (SanityManager.DEBUG)
+ {
+ // by shutting down system in debug mode, maybe
+ // we can catch root cause of the interrupt.
throw dataFactory.markCorrupt(se);
+ }
+ else
+ {
+ // No need to shut down runtime database on read
+ // error in delivered system, throwing exception
+ // should be enough. Thrown exception has nested
+ // IO exception which is root cause of error.
+ throw se;
+ }
}
}
}
@@ -584,14 +730,27 @@
}
- private void writePage(PageKey identity, boolean syncMe)
+ /**
+ * write the page from this CachedPage object to disk.
+ * <p>
+ *
+ * @param newIdentity indentity (ie. page number) of the page to read
+ * @param syncMe does the write of this single page have to be sync'd?
+ *
+ * @exception StandardException Standard exception policy.
+ **/
+ private void writePage(
+ PageKey identity,
+ boolean syncMe)
throws StandardException
{
- writeFormatId(identity); // make subclass write the page format
+ // make subclass write the page format
+ writeFormatId(identity);
- writePage(identity); // let subclass have a chance to write any cached
- // data to page data array
+ // let subclass have a chance to write any cached data to page data
+ // array
+ writePage(identity);
// force WAL - and check to see if database is corrupt or is frozen.
// last log Instant may be null if the page is being forced
@@ -600,18 +759,22 @@
LogInstant flushLogTo = getLastLogInstant();
dataFactory.flush(flushLogTo);
- if (flushLogTo != null) {
+ if (flushLogTo != null)
+ {
clearLastLogInstant();
}
// find the container and file access object
- FileContainer myContainer = (FileContainer) containerCache.find(identity.getContainerId());
-
- if (myContainer != null) {
- try {
+ FileContainer myContainer =
+ (FileContainer) containerCache.find(identity.getContainerId());
- myContainer.writePage(identity.getPageNumber(), pageData, syncMe);
+ if (myContainer != null)
+ {
+ try
+ {
+ myContainer.writePage(
+ identity.getPageNumber(), pageData, syncMe);
//
// Do some in memory unlogged bookkeeping tasks while we have
@@ -621,10 +784,10 @@
if (!isOverflowPage() && isDirty())
{
- // let the container knows whether this page is a not filled,
- // non-overflow page
- myContainer.trackUnfilledPage(identity.getPageNumber(),
- unfilled());
+ // let the container knows whether this page is a not
+ // filled, non-overflow page
+ myContainer.trackUnfilledPage(
+ identity.getPageNumber(), unfilled());
// if this is not an overflow page, see if the page's row
// count has changed since it come into the cache.
@@ -638,13 +801,19 @@
if (currentRowCount != initialRowCount)
{
- myContainer.updateEstimatedRowCount(currentRowCount-initialRowCount);
- setContainerRowCount(myContainer.getEstimatedRowCount(0));
+ myContainer.updateEstimatedRowCount(
+ currentRowCount - initialRowCount);
+
+ setContainerRowCount(
+ myContainer.getEstimatedRowCount(0));
+
initialRowCount = currentRowCount;
}
}
- } catch (IOException ioe) {
+ }
+ catch (IOException ioe)
+ {
// page cannot be written
throw StandardException.newException(
SQLState.FILE_WRITE_PAGE_EXCEPTION,
@@ -658,18 +827,22 @@
}
else
{
- StandardException nested = StandardException.newException(SQLState.DATA_CONTAINER_VANISHED, identity.getContainerId());
+ StandardException nested =
+ StandardException.newException(
+ SQLState.DATA_CONTAINER_VANISHED,
+ identity.getContainerId());
throw dataFactory.markCorrupt(
StandardException.newException(
SQLState.FILE_WRITE_PAGE_EXCEPTION, nested,
identity, new Integer(myContainer.getPageSize())));
}
- synchronized (this) {
- isDirty = false;
- preDirty = false;
+ synchronized (this)
+ {
+ // change page state to not dirty after the successful write
+ isDirty = false;
+ preDirty = false;
}
-
}
public void setContainerRowCount(long rowCount)
@@ -677,8 +850,6 @@
containerRowCount = rowCount;
}
-
-
/*
** if the page size is different from the page buffer, then make a
** new page buffer and make subclass use the new page buffer
@@ -686,7 +857,8 @@
protected void setPageArray(int pageSize) throws StandardException
{
- if ((pageData == null) || (pageData.length != pageSize)) {
+ if ((pageData == null) || (pageData.length != pageSize))
+ {
pageData = new byte[pageSize];
if (pageData == null || pageData.length != pageSize)
@@ -708,17 +880,18 @@
// initialize in memory structure using the read in buffer in pageData
- protected abstract void initFromData(FileContainer container, PageKey id) throws StandardException;
+ protected abstract void initFromData(FileContainer container, PageKey id)
+ throws StandardException;
// create the page
- protected abstract void createPage(PageKey id, int[] args) throws StandardException;
+ protected abstract void createPage(PageKey id, int[] args)
+ throws StandardException;
// page is about to be written, write everything to pageData array
protected abstract void writePage(PageKey id) throws StandardException;
// write out the formatId to the pageData
- protected abstract void writeFormatId(PageKey identity) throws StandardException;
-
-
+ protected abstract void writeFormatId(PageKey identity)
+ throws StandardException;
}