001    package edu.harvard.deas.hyperenc.vsat;
002    
003    import java.io.Serializable;
004    import java.security.SecureRandom;
005    
006    import edu.harvard.deas.hyperenc.util.NNLookup;
007    
008    // TODO:Fix the collision detection
009    // TODO:Sync access to the database
010    
011    /** Contains an index of the pages we will provide.  Does the
012        book-keeping to make sure that each page is only accessed twice,
013            returns pages based on the Nearest Neighbor lookup */
014    public class NNPageDatabase
015            extends Thread
016            implements Serializable, PageDatabase
017    {
018      private static final long serialVersionUID = 1L;
019      
020      /** Controls debugging output. */
021      private static final boolean DEBUG = false;
022            
023      /** 
024       * The number of pages this database will contain 
025       */
026      public static final int MAX_NUM_PAGES = 50;
027    
028      /** 
029       * Strong pseudo-random number generator for creating page id's
030       */
031      private SecureRandom securePRNG;
032      
033      /** 
034       * Our database of pages is a NNLookup table.  Each page is a VSatPage object 
035       **/
036      private NNLookup<VSatPage>      pageDB;
037      
038      /**
039       * Creates a new NNPageDatabase. Generates MAX_NUM_PAGES and inserts them
040       * into the database.
041       */
042      public NNPageDatabase()
043      {
044        pageDB = new NNLookup<VSatPage>();
045        securePRNG = new SecureRandom();
046        
047        // generate our initial complement of pages
048        for ( int i = 0; i < MAX_NUM_PAGES; i++ ) {
049                    if (DEBUG) System.out.println("Creating page " + i + " of " + MAX_NUM_PAGES);
050              generateNewPage();
051            }
052      }
053      
054      /**
055       * @return Number of pages currently stored in the page database.
056       */
057      public int getNumPages() {
058              return pageDB.size();
059      }
060      
061      /**
062       * @return Maximum number of pages that can be stored in the page database.
063       */
064      public int getMaxPages() {
065              return MAX_NUM_PAGES;
066      }
067      
068      /**
069       * Equivalent to <code>getPage</code>.
070       */
071      public byte[] getClosestPage(int key) {
072              return getPage(key);
073      }
074    
075      /** Use the key to return the closest available page.
076          @param key The key we are trying to match.
077          @return The page whose key was closest to the request, or
078            <code>null</code> if the database is empty.
079      */
080      public byte[] getPage(int key)
081      {
082        VSatPage thisPage;
083    
084        //get closest page
085        synchronized (pageDB) {
086          thisPage = pageDB.get(key);
087        }
088    
089        if (thisPage == null) { return null; }
090        
091        // increment the counter for that page
092        thisPage.increment();
093        
094        //if this page has been accessed twice, delete it from the db
095        int count = thisPage.getCount();
096        if ( count == 2 ) {
097          synchronized (pageDB) {
098            pageDB.delete(key);
099          }
100          // Generate a new page to replace the old one
101          this.generateNewPage();
102        } else if (count > 2) {
103          throw new IllegalStateException("Page access count is " + count
104              + ", which is greater than 2.");
105        }
106        
107        return thisPage.getData();
108      }
109    
110      /** Generate a new page and put it into the database */
111      private void generateNewPage()
112      {
113              // Spawn a thread to collect the page
114              PageCreator pc = new PageCreator();
115              pc.start();
116      }
117    
118      private class PageCreator extends Thread {
119              public void run() {
120                      boolean keepGoing;
121    
122                      //call a new TrueRandomSource to get file data
123                      TrueRandomSource    myFRS;
124                      // XXX coerce into using DevRandomSource... (undo me later)
125                      if (true && DevRandomSource.randomDevice() != null) {
126                              myFRS = new DevRandomSource();
127                              if (DEBUG)
128                                      System.out.println("Node: getting randomness from "+
129                                                      DevRandomSource.randomDevice());
130                      } else {
131                              //System.out.println("Node: getting randomness from FileRandomSource");
132                              myFRS = new FileRandomSource();
133                              if (DEBUG)
134                                      System.out.println("Node: getting randomness from FileRandomSource");
135                              //myFRS = new DevRandomSource();
136                      }
137    
138                      VSatPage newPage = null;
139                      int key;
140    
141                      do {
142                              byte[] fileRandom = null; // Hold the random file data
143    
144                              do {
145                                      fileRandom = myFRS.genRandomness();
146                              } while (fileRandom == null);
147    
148                              //create a new VSatPage with this data
149                              newPage = new VSatPage(fileRandom);
150    
151                              //we cannot permit collisions; that is,
152                              //two pages cannot have the same key value
153                              //so we need to find a unique key or just
154                              //replace the existing key-page pair
155                              //Rather than waste pages, or cause page collection
156                              // to fail for the client, we check for uniqueness
157                              // make a new key using our strong prng 
158                              key = securePRNG.nextInt();
159                              synchronized (pageDB) {
160                                      keepGoing = pageDB.getExact(key) != null;
161                              }
162                      } while (keepGoing);
163                      
164                      synchronized (pageDB) {
165                            pageDB.add(key, newPage);         
166                            if (DEBUG) System.out.println("Added a page");
167                      }
168              }
169      }
170              
171    
172    }
173