All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mckoi.database.StateStore Maven / Gradle / Ivy

Go to download

A full SQL database system with JDBC driver that can be embedded in a Java application or operate as a stand-alone server with clients connecting via TCP/IP.

The newest version!
/**
 * com.mckoi.database.StateStore  10 Feb 2003
 *
 * Mckoi SQL Database ( http://www.mckoi.com/database )
 * Copyright (C) 2000, 2001, 2002  Diehl and Associates, Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * Version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License Version 2 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * Version 2 along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * Change Log:
 * 
 * 
 */

package com.mckoi.database;

import com.mckoi.store.*;
import com.mckoi.util.ByteArrayUtil;
import java.io.*;
import java.util.ArrayList;
import com.mckoi.debug.DebugLogger;

/**
 * A store that manages the current state of all tables in a Conglomerate.  It
 * persistantly manages three pieces of information about a conglomerate - the
 * tables that are visible, the tables that are deleted, and a table_id value
 * assigned to new tables that are created.
 *
 * @author Tobias Downer
 */

class StateStore {

  /**
   * The MAGIC value used for state header areas.
   */
  private int MAGIC = 0x0BAC8001;

  /**
   * The Store object this state store wraps around.
   */
  private Store store;

  /**
   * The current table identifier.
   */
  private int table_id;

  /**
   * The header area of the state store.  The format of the header area is;
   *   MAGIC(4) - RESERVED(4) - TABLE_ID(8) -
   *   VISIBLE_TABLES_POINTER(8) - DELETED_TABLES_POINTER(8)
   */
  private MutableArea header_area;

  /**
   * Pointer to the visible table area in the store.
   */
  private long vis_p;
  
  /**
   * Pointer to the delete table area in the store.
   */
  private long del_p;
  
  /**
   * The list of visible state resources.
   */
  private ArrayList visible_list;

  /**
   * The list of deleted state resources.
   */
  private ArrayList delete_list;

  /**
   * Set to true if the visible list was changed.
   */
  private boolean vis_list_change;
  
  /**
   * Set to true if the delete list was changed.
   */
  private boolean del_list_change;

  /**
   * Constructs the StateStore.
   */
  public StateStore(Store store) {
    this.store = store;
    vis_list_change = false;
    del_list_change = false;
  }


  /**
   * Removes the given resource from the list.
   */
  private void removeResource(ArrayList list, String name) {
    int sz = list.size();
    for (int i = 0; i < sz; ++i) {
      StateResource resource = (StateResource) list.get(i);
      if (name.equals(resource.name)) {
        list.remove(i);
        return;
      }
    }
    throw new RuntimeException("Couldn't find resource '" + name + "' in list.");
  }

  /**
   * Reads the state resource list from the given area in the store.
   */
  private void readStateResourceList(ArrayList list, long pointer)
                                                          throws IOException {
    DataInputStream d_in = new DataInputStream(
                                            store.getAreaInputStream(pointer));
    int version = d_in.readInt();   // version
    int count = (int) d_in.readLong();
    for (int i = 0; i < count; ++i) {
      long table_id = d_in.readLong();
      String name = d_in.readUTF();
      StateResource resource = new StateResource(table_id, name);
      list.add(resource);
    }
    d_in.close();
  }

  /**
   * Writes the state resource list to the given area in the store.
   */
  private void writeStateResourceList(ArrayList list, DataOutputStream d_out)
                                                         throws IOException {
    d_out.writeInt(1);
    int sz = list.size();
    d_out.writeLong(sz);
    for (int i = 0; i < sz; ++i) {
      StateResource resource = (StateResource) list.get(i);
      d_out.writeLong(resource.table_id);
      d_out.writeUTF(resource.name);
    }
  }

  /**
   * Writes the given list to the store and returns a pointer to the area once
   * the write has finished.
   */
  private long writeListToStore(ArrayList list) throws IOException {
    ByteArrayOutputStream bout = new ByteArrayOutputStream();
    DataOutputStream d_out = new DataOutputStream(bout);
    writeStateResourceList(list, d_out);
    d_out.flush();
    d_out.close();

    byte[] buf = bout.toByteArray();

    AreaWriter a = store.createArea(buf.length);
    long list_p = a.getID();
    a.put(buf);
    a.finish();
    
    return list_p;
  }

  /**
   * Creates the state store in the store and returns a pointer to the header
   * used later for initializing the state.
   */
  public synchronized long create() throws IOException {
    // Allocate empty visible and deleted tables area
    AreaWriter vis_tables_area = store.createArea(12);
    AreaWriter del_tables_area = store.createArea(12);
    vis_p = vis_tables_area.getID();
    del_p = del_tables_area.getID();

    // Write empty entries for both of these
    vis_tables_area.putInt(1);
    vis_tables_area.putLong(0);
    vis_tables_area.finish();
    del_tables_area.putInt(1);
    del_tables_area.putLong(0);
    del_tables_area.finish();

    // Now allocate an empty state header
    AreaWriter header_writer = store.createArea(32);
    long header_p = header_writer.getID();
    header_writer.putInt(MAGIC);
    header_writer.putInt(0);
    header_writer.putLong(0);
    header_writer.putLong(vis_p);
    header_writer.putLong(del_p);
    header_writer.finish();

    header_area = store.getMutableArea(header_p);
    
    // Reset table_id
    table_id = 0;

    visible_list = new ArrayList();
    delete_list = new ArrayList();
    
    // Return pointer to the header area
    return header_p;
  }

  /**
   * Initializes the state store given a pointer to the header area in the
   * store.
   */
  public synchronized void init(long header_p) throws IOException {
    header_area = store.getMutableArea(header_p);
    int mag_value = header_area.getInt();
    if (mag_value != MAGIC) {
      throw new IOException("Magic value for state header area is incorrect.");
    }
    if (header_area.getInt() != 0) {
      throw new IOException("Unknown version for state header area.");
    }
    table_id = (int) header_area.getLong();
    vis_p = header_area.getLong();
    del_p = header_area.getLong();

    // Setup the visible and delete list
    visible_list = new ArrayList();
    delete_list = new ArrayList();

    // Read the resource list for the visible and delete list.
    readStateResourceList(visible_list, vis_p);
    readStateResourceList(delete_list, del_p);
    
  }

  /**
   * Reads a legacy state file (pre version 1) and converts it to a state store
   * format compatible with this store.  Fortunately the conversion is fairly
   * straight-forward.  This is otherwise the same as using the 'create' method.
   */
  public synchronized long convert(File legacy_sf, DebugLogger debug)
                                                           throws IOException {
    // Create a blank area in the store.
    long header_p = create();
                                                             
    // Open the state file.
    FixedSizeDataStore state_file =
                                new FixedSizeDataStore(legacy_sf, 507, debug);
    state_file.open(true);

    // Read the header.
    byte[] reserved_buffer = new byte[64];
    state_file.readReservedBuffer(reserved_buffer, 0, 64);

    // Read the list of visible tables....
    int tables_sector = ByteArrayUtil.getInt(reserved_buffer, 4);
    InputStream sin = state_file.getSectorInputStream(tables_sector);
    DataInputStream din = new DataInputStream(sin);
    int vtver = din.readInt();   // The version.
    int size = din.readInt();
    // For each committed table,
    for (int i = 0 ; i < size; ++i) {
      int table_id = din.readInt();
      String resource_name = din.readUTF();
      // Convert to new resource type
      if (!resource_name.startsWith(":")) {
        resource_name = ":1" + resource_name;
      }
      // Add this entry to the visible resource.
      addVisibleResource(new StateResource(table_id, resource_name));
    }
    din.close();
    
    // Read the list of dropped tables....
    int dropped_sector = ByteArrayUtil.getInt(reserved_buffer, 12);
    if (dropped_sector > -1) {
      sin = state_file.getSectorInputStream(dropped_sector);
      din = new DataInputStream(sin);
      int dsver = din.readInt();  // The version.
      size = din.readInt();
      // For each deleted table file name,
      for (int i = 0; i < size; ++i) {
        String resource_name = din.readUTF();
        // Convert to new resource type
        if (!resource_name.startsWith(":")) {
          resource_name = ":1" + resource_name;
        }
        // Add this entry to the delete resource.
        addDeleteResource(new StateResource(-1, resource_name));
      }
      din.close();

    }

    // The sector that contains state information (the table id value)....
    int state_sector = ByteArrayUtil.getInt(reserved_buffer, 8);
    sin = state_file.getSectorInputStream(state_sector);
    din = new DataInputStream(sin);
    din.readInt();   // The version
    int conv_table_id = din.readInt();
    din.close();

    // Close the state file.
    state_file.close();
    
    // Update the table_id state
    header_area.position(8);
    header_area.putLong(conv_table_id);
    // Check out the change
    header_area.checkOut();
    
    // Finally commit the changes
    commit();
    
    // Return a pointer to the structure
    return header_p;
  }

  /**
   * Returns the next table id and increments the table_id counter.
   */
  public synchronized int nextTableID() throws IOException {
    int cur_counter = table_id;
    ++table_id;

    try {
      store.lockForWrite();

      // Update the state in the file
      header_area.position(8);
      header_area.putLong(table_id);
      // Check out the change
      header_area.checkOut();

    }
    finally {
      store.unlockForWrite();
    }

    return cur_counter;
  }

  /**
   * Returns a list of all table resources that are currently in the visible
   * list.
   */
  public synchronized StateResource[] getVisibleList() {
    return (StateResource[])
                 visible_list.toArray(new StateResource[visible_list.size()]);
  }
  
  /**
   * Returns a list of all table resources that are currently in the deleted
   * list.
   */
  public synchronized StateResource[] getDeleteList() {
    return (StateResource[])
                   delete_list.toArray(new StateResource[delete_list.size()]);
  }

  /**
   * Returns true if the visible list contains a state resource with the given
   * table id value.
   */
  public synchronized boolean containsVisibleResource(int table_id) {
    int sz = visible_list.size();
    for (int i = 0; i < sz; ++i) {
      if (((StateResource) visible_list.get(i)).table_id == table_id) {
        return true;
      }
    }
    return false;
  }
  
  /**
   * Adds the given StateResource to the visible table list.  This does not
   * persist the state.  To persist this change a call to 'commit' must be
   * called.
   */
  public synchronized void addVisibleResource(StateResource resource) {
    visible_list.add(resource);
    vis_list_change = true;
  }
  
  /**
   * Adds the given StateResource to the deleted table list.  This does not
   * persist the state.  To persist this change a call to 'commit' must be
   * called.
   */
  public synchronized void addDeleteResource(StateResource resource) {
    delete_list.add(resource);
    del_list_change = true;
  }
  
  /**
   * Removes the resource with the given name from the visible list.  This does
   * not persist the state.  To persist this change a call to 'commit' must be
   * called.
   */
  public synchronized void removeVisibleResource(String name) {
    removeResource(visible_list, name);
    vis_list_change = true;
  }
  
  /**
   * Removes the resource with the given name from the deleted list.  This does
   * not persist the state.  To persist this change a call to 'commit' must be
   * called.
   */
  public synchronized void removeDeleteResource(String name) {
    removeResource(delete_list, name);
    del_list_change = true;
  }

  /**
   * Commits the current state to disk so that it makes a persistent change to
   * the state.  A further call to 'synch()' will synchronize the file.  This
   * will only commit changes if there were modifications to the state.
   * Returns true if this commit caused any changes to the persistant state.
   */
  public synchronized boolean commit() throws IOException {
    boolean changes = false;
    long new_vis_p = vis_p;
    long new_del_p = del_p;
    
    try {
      store.lockForWrite();

      // If the lists changed, then write new state areas to the store.
      if (vis_list_change) {
        new_vis_p = writeListToStore(visible_list);
        vis_list_change = false;
        changes = true;
      }
      if (del_list_change) {
        new_del_p = writeListToStore(delete_list);
        del_list_change = false;
        changes = true;
      }
      // Commit the changes,
      if (changes) {
        header_area.position(16);
        header_area.putLong(new_vis_p);
        header_area.putLong(new_del_p);
        // Check out the change.
        header_area.checkOut();
        if (vis_p != new_vis_p) {
          store.deleteArea(vis_p);
          vis_p = new_vis_p;
        }
        if (del_p != new_del_p) {
          store.deleteArea(del_p);
          del_p = new_del_p;
        }
      }

    }
    finally {
      store.unlockForWrite();
    }

    return changes;
  }

  // ---------- Inner classes ----------
  
  /**
   * Represents a single StateResource in either a visible or delete list in
   * this state file.
   */
  static class StateResource {

    /**
     * The unique identifier for the resource.
     */
    long table_id;

    /**
     * The unique name given to the resource to distinguish it from all other
     * resources.
     */
    String name;

    public StateResource(long table_id, String name) {
      this.table_id = table_id;
      this.name = name;
    }
    
  }
  
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy