Comparing AspectJ and IIIA on the Checksum Feature from Berkeley DB

Finally, we compare AspectJ and IIIA on a concrete feature from Berkeley DB. First we directly compare the original AspectJ implementation (left column) with an IIIA implementation (right column). Additionally, we show how we had to prepare the source code for the IIIA solution below. The main differences are highlighted.

The complete IIIA implementation can be downloaded here. The AspectJ implementation is available as part of the refactored Berkeley DB project here.

Checksum-Feature in AspectJ

Checksum-Feature in IIIA

public privileged aspect ChecksumValidatorAspect {
aspect Checksum advises 
    LogManagerInitializationJP, 
    ValidateHeaderJP,
    ValidateBodyJP,
    LogManagerStaticInitJP,
    FileReaderInitializationJP,
    FileReaderValidateChecksumJP,
    FileReaderDeterminIsCollectDataJP,
    FileReaderReadHeaderJP,
    FileReaderStartChecksumJP {
  after(LogManager logManager, EnvironmentImpl env) 
                              throws DatabaseException : 
       execution(LogManager.new(EnvironmentImpl, boolean)) 
       && this(logManager) && args(env,boolean) {
    /* See if we're configured to do a checksum when 
       reading in objects. */
    DbConfigManager configManager = env.getConfigManager();
    logManager.doChecksumOnRead = configManager
        .getBoolean(EnvironmentParams.LOG_CHECKSUM_READ);

  }

  private ChecksumValidator validator = null;

  after(LogManager lm, ByteBuffer entryBuffer, 
           EntryHeader entryHeader) throws DatabaseException :
    call(void EntryHeader.readHeader(ByteBuffer, boolean)) && 
    this(lm) && args(entryBuffer,boolean) && 
    target(entryHeader) 
  {
    if (lm.doChecksumOnRead) {
      validator = new ChecksumValidator();
      int oldpos = entryBuffer.position();
      entryBuffer.position(oldpos - LogManager.HEADER_CONTENT_BYTES);
      validator.update(lm.envImpl, entryBuffer,
          LogManager.HEADER_CONTENT_BYTES, false);
      entryBuffer.position(oldpos);
    }
  }

  private pointcut getLogEntryFromLogSource(long lsn) :
    execution(LogEntry LogManager.getLogEntryFromLogSource(long,LogSource)) 
    && args(lsn,LogSource);

  after(LogManager lm, ByteBuffer entryBuffer, 
      EntryHeader entryHeader, long lsn) throws DatabaseException :
    call(ByteBuffer LogManager.getRemainingBuffer(ByteBuffer, LogSource, long, EntryHeader))
    && target(lm) && args(entryBuffer, LogSource, long, entryHeader) && 
    cflow(getLogEntryFromLogSource(lsn)) 
  {
    if (lm.doChecksumOnRead) {
      /* Check the checksum first. */
      validator.update(lm.envImpl, entryBuffer, entryHeader
          .getEntrySize(), false);
      validator.validate(lm.envImpl, entryHeader.getChecksum(), lsn);
    }

  }

  public boolean LogManager.doChecksumOnRead;
  public boolean LogManager.getChecksumOnRead() {
    return doChecksumOnRead;
  }
  after(LogManagerInitializationJP l) {
    /* See if we're configured to do a checksum when 
       reading in objects. */
    DbConfigManager configManager = l.env.getConfigManager();
    l._this.doChecksumOnRead = configManager
        .getBoolean(EnvironmentParams.LOG_CHECKSUM_READ);
  }
  
  private ChecksumValidator validator;
  private long storedChecksum;
  
  after(ValidateHeaderJP l) throws DatabaseException {
    validator = null;
    storedChecksum = LogUtils.getUnsignedInt(l.entryBuffer);
    if (l._this.doChecksumOnRead) {
      validator = new ChecksumValidator();
      validator.update(l._this.envImpl, l.entryBuffer, 
        LogManager.HEADER_CONTENT_BYTES, false);
    }

  }
  after(ValidateBodyJP l) throws DatabaseException {
    if (l._this.doChecksumOnRead) {
       /* Check the checksum first. */
       validator.update(l._this.envImpl, l.entryBuffer, 
                        l.itemSize, false);
       validator.validate(l._this.envImpl, storedChecksum, l.lsn);
     }
  }
  
  public boolean LogManager.doChecksumOnRead;
  public boolean LogManager.getChecksumOnRead() {
    return doChecksumOnRead;
  }
  static final int LogManager.CHECKSUM_BYTES = 4; 
  // size of checksum field

  static final int LogManager.HEADER_CHECKSUM_OFFSET = 0; 
  // size of checksum

  // field

  after(): staticinitialization(LogManager) {
    LogManager.HEADER_BYTES += LogManager.CHECKSUM_BYTES; 
    // size of entry
    // header

    LogManager.PREV_BYTES = 4; // size of previous field

    LogManager.HEADER_CONTENT_BYTES = LogManager.HEADER_BYTES
        - LogManager.CHECKSUM_BYTES;

    LogManager.HEADER_ENTRY_TYPE_OFFSET += 4;

    LogManager.HEADER_VERSION_OFFSET += 4;

    LogManager.HEADER_PREV_OFFSET += 4;

    LogManager.HEADER_SIZE_OFFSET += 4;
  }
  static final int LogManager.CHECKSUM_BYTES = 4; 
  // size of checksum field

  static final int LogManager.HEADER_CHECKSUM_OFFSET = 0; 
  // size of checksum

  // field

  after(LogManagerStaticInitJP j) {
    LogManager.HEADER_BYTES += LogManager.CHECKSUM_BYTES; 
    // size of entry
    // header

    LogManager.PREV_BYTES = 4; // size of previous field

    LogManager.HEADER_CONTENT_BYTES = LogManager.HEADER_BYTES
        - LogManager.CHECKSUM_BYTES;

    LogManager.HEADER_ENTRY_TYPE_OFFSET += 4;

    LogManager.HEADER_VERSION_OFFSET += 4;

    LogManager.HEADER_PREV_OFFSET += 4;

    LogManager.HEADER_SIZE_OFFSET += 4;
  }
  Object around(FileReader fileReader, EnvironmentImpl env)
      throws DatabaseException :
    execution(FileReader.new(EnvironmentImpl, int, boolean, long, Long, long,long)) && 
    args(env,int, boolean, long, Long, long,long) && 
    this(fileReader) && within(FileReader)
  {
    fileReader.doValidateChecksum = env.getLogManager().
                                       getChecksumOnRead();
    Object r = proceed(fileReader, env);

    if (fileReader.doValidateChecksum) {
      fileReader.cksumValidator = new ChecksumValidator();
    }
    fileReader.anticipateChecksumErrors = false;
    return r;
  }

  after(FileReader fileReader, ByteBuffer dataBuffer)
      throws DatabaseException : 
    call(void FileReader.hook_startChecksumValidation(ByteBuffer)) 
    && target(fileReader) && args(dataBuffer){

    boolean doValidate = fileReader.doValidateChecksum
        && (fileReader.alwaysValidateChecksum || fileReader
            .isTargetEntry(fileReader.currentEntryTypeNum,
                fileReader.currentEntryTypeVersion));

    if (doValidate) {
      fileReader.startChecksum(dataBuffer);
    }

    if (doValidate)
      fileReader.currentEntryCollectData = true;
  }

  after(FileReader fileReader, ByteBuffer dataBuffer)
      throws DatabaseException : 
    call(void FileReader.hook_checksumValidation(ByteBuffer)) && 
    target(fileReader) && args(dataBuffer){

    boolean doValidate = fileReader.doValidateChecksum
        && (fileReader.alwaysValidateChecksum || fileReader
            .isTargetEntry(fileReader.currentEntryTypeNum,
                fileReader.currentEntryTypeVersion));

    if (doValidate) {
      fileReader.validateChecksum(dataBuffer);
    }
  }


  before(ByteBuffer dataBuffer, FileReader fileReader):
    execution(void FileReader.readHeader(ByteBuffer)) && 
    args(dataBuffer) && target(fileReader) 
  {
    /* Get the checksum for this log entry. */
    fileReader.currentEntryChecksum = LogUtils.getUnsignedInt
                                             (dataBuffer);
  }

  before(FileReaderInitializationJP j) throws DatabaseException {
    j._this.doValidateChecksum = j.env.getLogManager().
                                      getChecksumOnRead();
  }

  after(FileReaderInitializationJP j) {
    if (j._this.doValidateChecksum) {
      j._this.cksumValidator = new ChecksumValidator();
    }
    j._this.anticipateChecksumErrors = false;
  }
  
  boolean around(FileReaderDeterminIsCollectDataJP j) {
    return proceed() || j._this.doValidateChecksum && 
                            j._this.alwaysValidateChecksum;
  }
  
  before(FileReaderStartChecksumJP j) throws DatabaseException {
    boolean doValidate = j._this.doValidateChecksum
      && (j.isTargetEntry || j._this.alwaysValidateChecksum);
    if (doValidate) {
      j._this.startChecksum(j.dataBuffer);
    }
  }

  before(FileReaderValidateChecksumJP j) throws DatabaseException {
    boolean doValidate = j._this.doValidateChecksum
      && (j.isTargetEntry || j._this.alwaysValidateChecksum);
    if (doValidate) {
      j._this.validateChecksum(j.dataBuffer);
    }
  }

  before(FileReaderReadHead&&erJP j){
    j._this.currentEntryChecksum = LogUtils.getUnsignedInt
                                            (j.dataBuffer);
  }
  public ChecksumValidator FileReader.cksumValidator;
  public boolean FileReader.doValidateChecksum; 
  public boolean FileReader.alwaysValidateChecksum; 
  public boolean FileReader.anticipateChecksumErrors;
  public void FileReader.startChecksum(ByteBuffer dataBuffer)
      throws DatabaseException {
    cksumValidator.reset();
    int entryStart = threadSafeBufferPosition(dataBuffer);
    System.out.println(dataBuffer);
    dataBuffer.reset();
    cksumValidator.update(env, dataBuffer, 
         LogManager.HEADER_CONTENT_BYTES, anticipateChecksumErrors);
    threadSafeBufferPosition(dataBuffer, entryStart);
  }
  public long FileReader.currentEntryChecksum;
  public void FileReader.validateChecksum(ByteBuffer entryBuffer)
      throws DatabaseException {

    cksumValidator.update(env, entryBuffer, currentEntrySize,
        anticipateChecksumErrors);
    cksumValidator.validate(env, currentEntryChecksum, 
         readBufferFileNum, currentEntryOffset, 
         anticipateChecksumErrors);
  }
}//end of aspect
  public ChecksumValidator FileReader.cksumValidator;
  public boolean FileReader.doValidateChecksum; 
  public boolean FileReader.alwaysValidateChecksum; 
  public boolean FileReader.anticipateChecksumErrors;
  public void FileReader.startChecksum(ByteBuffer dataBuffer)
      throws DatabaseException {
    cksumValidator.reset();
    int entryStart = threadSafeBufferPosition(dataBuffer);
    System.out.println(dataBuffer);
    dataBuffer.reset();
    cksumValidator.update(env, dataBuffer, 
         LogManager.HEADER_CONTENT_BYTES, anticipateChecksumErrors);
    threadSafeBufferPosition(dataBuffer, entryStart);
  }
  public long FileReader.currentEntryChecksum;
  public void FileReader.validateChecksum(ByteBuffer entryBuffer)
      throws DatabaseException {

    cksumValidator.update(env, entryBuffer, currentEntrySize,
        anticipateChecksumErrors);
    cksumValidator.validate(env, currentEntryChecksum, 
         readBufferFileNum, currentEntryOffset, 
         anticipateChecksumErrors);
  }
}//end of aspect

Join Point Types in IIIA

abstract public joinpointtype ChecksumJP { }

abstract public joinpointtype FileReaderChecksumJP 
    extends ChecksumJP {
  FileReader _this;
}

public joinpointtype FileReaderDeterminIsCollectDataJP 
    extends FileReaderChecksumJP {
}

public joinpointtype FileReaderInitializationJP 
    extends FileReaderChecksumJP {
  EnvironmentImpl env;
}

public joinpointtype FileReaderReadHeaderJP 
    extends FileReaderChecksumJP {
  ByteBuffer dataBuffer;
}

public joinpointtype FileReaderStartChecksumJP 
    extends FileReaderChecksumJP {
  boolean isTargetEntry;
  ByteBuffer dataBuffer;
}

public joinpointtype FileReaderValidateChecksumJP 
    extends FileReaderChecksumJP {
  boolean isTargetEntry;
  ByteBuffer dataBuffer;
}

abstract public joinpointtype LogManagerChecksumJP 
    extends ChecksumJP {
  LogManager _this;
}

public joinpointtype LogManagerInitializationJP 
    extends LogManagerChecksumJP {
  EnvironmentImpl env;
}

public joinpointtype LogManagerStaticInitJP 
    extends ChecksumJP {
}

public joinpointtype ValidateBodyJP 
    extends LogManagerChecksumJP {
  ByteBuffer entryBuffer;
  int itemSize;
  long lsn;
}

public joinpointtype ValidateHeaderJP 
    extends LogManagerChecksumJP {
  ByteBuffer entryBuffer;
}

LogManager in AspectJ (excerpt)

LogManager in IIIA (excerpt)

public class FileReader {

  public FileReader(EnvironmentImpl env, int readBufferSize, boolean forward,
      long startLsn, Long singleFileNumber, long endOfFileLsn,
      long finishLsn) throws IOException, DatabaseException {

    this.env = env;
    this.fileManager = env.getFileManager();
    //...
  }


  public boolean readNextEntry() throws DatabaseException, IOException {
    boolean foundEntry = false;
    try {
      while ((!eof) && (!foundEntry)) {
        ByteBuffer dataBuffer = readData(LogManager.HEADER_BYTES, true);
        readHeader(dataBuffer);
        boolean isTargetEntry = isTargetEntry(currentEntryTypeNum,
            currentEntryTypeVersion);
        currentEntryCollectData = isTargetEntry;
        hook_startChecksumValidation(dataBuffer);
        dataBuffer = readData(currentEntrySize, currentEntryCollectData);
        dataBuffer.mark();
        if (forward) {
          currentEntryOffset = nextEntryOffset;
          nextEntryOffset += LogManager.HEADER_BYTES
              + currentEntrySize;
        }
        hook_checksumValidation(dataBuffer);
        if (isTargetEntry) {
          if (processEntry(dataBuffer)) {
            foundEntry = true;
            nRead++;
          }
        } else if (currentEntryCollectData) {
          threadSafeBufferPosition(dataBuffer,
              threadSafeBufferPosition(dataBuffer)
                  + currentEntrySize);
        }
      }
    } catch (EOFException e) {
      eof = true;
    } catch (DatabaseException e) {
      eof = true;
      /* Report on error. */
      throw e;
    }
    return foundEntry;
  }
  
  private void hook_startChecksumValidation(ByteBuffer dataBuffer)
      throws DatabaseException {
  }

  private void hook_checksumValidation(ByteBuffer dataBuffer)
      throws DatabaseException {
  }

  private void readHeader(ByteBuffer dataBuffer) throws DatabaseException {
    dataBuffer.mark();
    currentEntryTypeNum = dataBuffer.get();
    currentEntryTypeVersion = dataBuffer.get();
    currentEntryPrevOffset = LogUtils.getUnsignedInt(dataBuffer);
    currentEntrySize = LogUtils.readInt(dataBuffer);
  }

public class FileReader exhibits 
    FileReaderInitializationJP,
    FileReaderValidateChecksumJP,
    FileReaderDeterminIsCollectDataJP,
    FileReaderReadHeaderJP,
    FileReaderStartChecksumJP {
  
  pointcut FileReaderInitializationJP : 
    execution(new(..)) && this(_this) && args(env,..);

  public FileReader(EnvironmentImpl env, int readBufferSize, boolean forward,
      long startLsn, Long singleFileNumber, long endOfFileLsn,
      long finishLsn) throws IOException, DatabaseException {

    this.env = env;
    this.fileManager = env.getFileManager();
    //...
  }

  public boolean readNextEntry() throws DatabaseException, IOException {
    boolean foundEntry = false;
    try {
      while ((!eof) && (!foundEntry)) {
        ByteBuffer dataBuffer = readData(LogManager.HEADER_BYTES, true);
        readHeader(dataBuffer);
	boolean isTargetEntry = isTargetEntry(currentEntryTypeNum, 
                                 currentEntryTypeVersion);
        boolean collectData = hook_isCollectData(isTargetEntry);
        exhibit new FileReaderStartChecksumJP(this,isTargetEntry,dataBuffer){};
        dataBuffer = readData(currentEntrySize, collectData);
        if (forward) {
          currentEntryOffset = nextEntryOffset;
          nextEntryOffset += LogManager.HEADER_BYTES
              + currentEntrySize;
        }
        exhibit new FileReaderValidateChecksumJP(this,isTargetEntry,dataBuffer){};
        if (isTargetEntry) {
          if (processEntry(dataBuffer)) {
            foundEntry = true;
            nRead++;
          }
        } else if (collectData) {
          threadSafeBufferPosition(dataBuffer,
              threadSafeBufferPosition(dataBuffer)
                  + currentEntrySize);
        }
      }
    } catch (EOFException e) {
      eof = true;
    } catch (DatabaseException e) {
      eof = true;
      /* Report on error. */
      throw e;
    }
    return foundEntry;
  }

  pointcut FileReaderDeterminIsCollectDataJP : 
          execution(boolean hook_isCollectData(..)) && this(_this);
  public boolean hook_isCollectData(boolean isTargetEntry) {
    return isTargetEntry; 
  }

  pointcut FileReaderReadHeaderJP : 
	  execution(void readHeader(ByteBuffer)) && 
          args(dataBuffer) && this(_this);
  private void readHeader(ByteBuffer dataBuffer) throws DatabaseException {
    dataBuffer.mark();
    currentEntryTypeNum = dataBuffer.get();
    currentEntryTypeVersion = dataBuffer.get();
    currentEntryPrevOffset = LogUtils.getUnsignedInt(dataBuffer);
    currentEntrySize = dataBuffer.getInt();
  }

  //...
}

Back to main page