View Javadoc

1   /**
2    * Copyright (c) 2011, University of Konstanz, Distributed Systems Group
3    * All rights reserved.
4    * 
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions are met:
7    * * Redistributions of source code must retain the above copyright
8    * notice, this list of conditions and the following disclaimer.
9    * * Redistributions in binary form must reproduce the above copyright
10   * notice, this list of conditions and the following disclaimer in the
11   * documentation and/or other materials provided with the distribution.
12   * * Neither the name of the University of Konstanz nor the
13   * names of its contributors may be used to endorse or promote products
14   * derived from this software without specific prior written permission.
15   * 
16   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19   * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
20   * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   */
27  
28  package org.treetank.io;
29  
30  import static com.google.common.base.Objects.toStringHelper;
31  
32  import java.io.File;
33  import java.util.Iterator;
34  import java.util.Map.Entry;
35  import java.util.Set;
36  
37  import org.treetank.access.conf.ResourceConfiguration;
38  import org.treetank.api.IMetaEntryFactory;
39  import org.treetank.api.IDataFactory;
40  import org.treetank.exception.TTIOException;
41  import org.treetank.io.LogKey.LogKeyBinding;
42  import org.treetank.io.LogValue.LogValueBinding;
43  
44  import com.google.common.cache.Cache;
45  import com.google.common.cache.CacheBuilder;
46  import com.google.common.cache.RemovalCause;
47  import com.google.common.cache.RemovalListener;
48  import com.google.common.cache.RemovalNotification;
49  import com.sleepycat.je.Cursor;
50  import com.sleepycat.je.Database;
51  import com.sleepycat.je.DatabaseConfig;
52  import com.sleepycat.je.DatabaseEntry;
53  import com.sleepycat.je.DatabaseException;
54  import com.sleepycat.je.Environment;
55  import com.sleepycat.je.EnvironmentConfig;
56  import com.sleepycat.je.LockMode;
57  import com.sleepycat.je.OperationStatus;
58  
59  /**
60   * An LRU cache, based on <code>LinkedHashMap</code>. This cache can hold an
61   * possible second cache as a second layer for example for storing data in a
62   * persistent way.
63   * 
64   * @author Sebastian Graf, University of Konstanz
65   */
66  public final class LRULog implements ILog {
67      //
68      // // START DEBUG CODE
69      // private final static File insertFile = new File("/Users/sebi/Desktop/runtimeResults/insert.txt");
70      // private final static File getFile = new File("/Users/sebi/Desktop/runtimeResults/get.txt");
71      //
72      // static final FileWriter insert;
73      // static final FileWriter get;
74      //
75      // static {
76      // try {
77      // insert = new FileWriter(insertFile);
78      // get = new FileWriter(getFile);
79      // } catch (IOException e) {
80      // throw new RuntimeException(e);
81      // }
82      // }
83  
84      /**
85       * Name for the database.
86       */
87      private static final String NAME = "berkeleyCache";
88  
89      /**
90       * Binding for the key, which is the databucket.
91       */
92      private final transient LogKeyBinding mKeyBinding;
93  
94      /**
95       * Binding for the value which is a bucket with related datas.
96       */
97      private final transient LogValueBinding mValueBinding;
98  
99      /**
100      * Berkeley Environment for the database.
101      */
102     private final transient Environment mEnv;
103 
104     /**
105      * Berkeley database.
106      */
107     private final transient Database mDatabase;
108 
109     /** Location to the BDB. */
110     private final transient File mLocation;
111 
112     /** Transient cache for buffering buckets. */
113     private final Cache<LogKey, LogValue> mCache;
114 
115     /** DB to select to support non-blocking writes. */
116     private int mSelected_db;
117 
118     /** Flag if closed. */
119     private boolean mClosed;
120 
121     /**
122      * Creates a new LRU cache.
123      * 
124      * @param pFile
125      *            Location of the cache.
126      * @param pDataFac
127      *            DataFactory for generating datas adhering to the used interface
128      * @param pMetaFac
129      *            MetaFactory for generating meta-entries adhering to the used interface
130      * @throws TTIOException
131      * 
132      */
133     public LRULog(final File pFile, final IDataFactory pDataFac, final IMetaEntryFactory pMetaFac)
134         throws TTIOException {
135         mClosed = false;
136         mKeyBinding = new LogKeyBinding();
137         mValueBinding = new LogValueBinding(pDataFac, pMetaFac);
138         mSelected_db = 1;
139 
140         final File classicLogLocation =
141             new File(pFile, ResourceConfiguration.Paths.TransactionLog.getFile().getName());
142 
143         if (classicLogLocation.list().length > 0) {
144             mLocation = new File(pFile, "_" + ResourceConfiguration.Paths.TransactionLog.getFile().getName());
145             mLocation.mkdirs();
146             mSelected_db = 2;
147         } else {
148             mLocation = classicLogLocation;
149         }
150 
151         try {
152             EnvironmentConfig config = new EnvironmentConfig();
153             config.setAllowCreate(true);
154             config = config.setSharedCache(false);
155             config.setLocking(false);
156             config.setCachePercent(20);
157             mEnv = new Environment(mLocation, config);
158             mEnv.cleanLog();
159             final DatabaseConfig dbConfig = new DatabaseConfig();
160             dbConfig.setAllowCreate(true);
161             dbConfig.setExclusiveCreate(true);
162             mDatabase = mEnv.openDatabase(null, NAME + mSelected_db, dbConfig);
163 
164         } catch (final DatabaseException exc) {
165             throw new TTIOException(exc);
166         }
167 
168         mCache =
169             CacheBuilder.newBuilder().maximumSize(100).removalListener(
170                 new RemovalListener<LogKey, LogValue>() {
171                     @Override
172                     public void onRemoval(RemovalNotification<LogKey, LogValue> notification) {
173                         if (notification.getCause() != RemovalCause.REPLACED) {
174                             insertIntoBDB(notification.getKey(), notification.getValue());
175                         }
176 
177                     }
178                 }).build();
179     }
180 
181     /**
182      * {@inheritDoc}
183      */
184     public synchronized LogValue get(final LogKey pKey) throws TTIOException {
185         if (isClosed()) {
186             return new LogValue(null, null);
187         }
188         LogValue val = mCache.getIfPresent(pKey);
189         if (val == null || val.getModified() == null) {
190             final DatabaseEntry valueEntry = new DatabaseEntry();
191             final DatabaseEntry keyEntry = new DatabaseEntry();
192             mKeyBinding.objectToEntry(pKey, keyEntry);
193             try {
194                 final OperationStatus status = mDatabase.get(null, keyEntry, valueEntry, LockMode.DEFAULT);
195                 if (status == OperationStatus.SUCCESS) {
196                     val = mValueBinding.entryToObject(valueEntry);
197                 } else {
198                     val = new LogValue(null, null);
199                 }
200                 mCache.put(pKey, val);
201                 // get.write(pKey.getLevel() + "," + pKey.getSeq() + "\n");
202                 // get.flush();
203 
204             } catch (DatabaseException exc) {
205                 throw new TTIOException(exc);
206             }
207         }
208         return val;
209     }
210 
211     /**
212      * {@inheritDoc}
213      */
214     public void put(final LogKey pKey, final LogValue pValue) throws TTIOException {
215         mCache.put(pKey, pValue);
216     }
217 
218     /**
219      * {@inheritDoc}
220      */
221     public synchronized void close() throws TTIOException {
222         try {
223             mClosed = true;
224             mDatabase.close();
225             mEnv.removeDatabase(null, NAME + mSelected_db);
226             mEnv.close();
227             IOUtils.recursiveDelete(mLocation);
228             mLocation.mkdir();
229 
230         } catch (final DatabaseException exc) {
231             throw new TTIOException(exc);
232         }
233     }
234 
235     /**
236      * {@inheritDoc}
237      */
238     public synchronized boolean isClosed() {
239         return mClosed;
240     }
241 
242     /**
243      * {@inheritDoc}
244      */
245     @Override
246     public String toString() {
247         return toStringHelper(this).add("mDatabase", mDatabase).toString();
248     }
249 
250     /**
251      * Returning all elements as Iterator.
252      * 
253      * @return new LogIterator-instance
254      */
255     public LogIterator getIterator() {
256         Set<Entry<LogKey, LogValue>> entries = mCache.asMap().entrySet();
257         for (Entry<LogKey, LogValue> entry : entries) {
258             insertIntoBDB(entry.getKey(), entry.getValue());
259         }
260 
261         return new LogIterator();
262     }
263 
264     private void insertIntoBDB(LogKey pKey, LogValue pVal) {
265         if (pVal.getModified() != null) {
266             final DatabaseEntry valueEntry = new DatabaseEntry();
267             final DatabaseEntry keyEntry = new DatabaseEntry();
268             mKeyBinding.objectToEntry(pKey, keyEntry);
269             mValueBinding.objectToEntry(pVal, valueEntry);
270             try {
271 
272                 // ////TODO DEBUGCODE///////
273                 // final DatabaseEntry valueOld = new DatabaseEntry();
274                 // OperationStatus status2 = mDatabase.get(null, keyEntry, valueOld, LockMode.DEFAULT);
275                 // if (status2 == OperationStatus.SUCCESS) {
276                 // System.out.println(mDatabase.count());
277                 // status2 = mDatabase.delete(null, keyEntry);
278                 // System.out.println(mDatabase.count());
279                 // status2 = mDatabase.get(null, keyEntry, valueOld, LockMode.DEFAULT);
280                 // System.out.println(mDatabase.count());
281                 // mDatabase.sync();
282                 // System.out.println(mDatabase.count());
283                 // }
284 
285                 // OperationStatus status = mDatabase.put(null, keyEntry, valueEntry);
286                 mDatabase.put(null, keyEntry, valueEntry);
287                 // insert.write(pKey.getLevel() + "," + pKey.getSeq() + "\n");
288                 // insert.flush();
289 
290             } catch (DatabaseException exc) {
291                 throw new RuntimeException(exc);
292             }
293         }
294     }
295 
296     class LogIterator implements Iterator<LogValue>, Iterable<LogValue> {
297 
298         private Cursor mCursor;
299         private DatabaseEntry valueEntry;
300         private DatabaseEntry keyEntry;
301 
302         /**
303          * 
304          * Constructor.
305          * 
306          */
307         public LogIterator() {
308             mCursor = mDatabase.openCursor(null, null);
309         }
310 
311         /**
312          * {@inheritDoc}
313          */
314         @Override
315         public Iterator<LogValue> iterator() {
316             return this;
317         }
318 
319         /**
320          * {@inheritDoc}
321          */
322         @Override
323         public boolean hasNext() {
324             boolean returnVal = false;
325             valueEntry = new DatabaseEntry();
326             keyEntry = new DatabaseEntry();
327             try {
328                 final OperationStatus status = mCursor.getNext(keyEntry, valueEntry, LockMode.DEFAULT);
329                 if (status == OperationStatus.SUCCESS) {
330                     returnVal = true;
331                 }
332             } catch (final DatabaseException exc) {
333                 throw new RuntimeException(exc);
334             }
335             if (returnVal == false) {
336                 mCursor.close();
337             }
338             return returnVal;
339         }
340 
341         /**
342          * {@inheritDoc}
343          */
344         @Override
345         public LogValue next() {
346             LogValue val;
347             if ((val = mCache.getIfPresent(keyEntry)) != null) {
348                 return val;
349             }
350             return mValueBinding.entryToObject(valueEntry);
351         }
352 
353         /**
354          * {@inheritDoc}
355          */
356         @Override
357         public void remove() {
358             throw new UnsupportedOperationException();
359         }
360 
361     }
362 
363 }