001: // kelondroDynTree.java
002: // -----------------------
003: // part of The Kelondro Database
004: // (C) by Michael Peter Christen; mc@anomic.de
005: // first published on http://www.anomic.de
006: // Frankfurt, Germany, 2004
007: // last major change: 20.09.2004
008: //
009: // This program is free software; you can redistribute it and/or modify
010: // it under the terms of the GNU General Public License as published by
011: // the Free Software Foundation; either version 2 of the License, or
012: // (at your option) any later version.
013: //
014: // This program is distributed in the hope that it will be useful,
015: // but WITHOUT ANY WARRANTY; without even the implied warranty of
016: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
017: // GNU General Public License for more details.
018: //
019: // You should have received a copy of the GNU General Public License
020: // along with this program; if not, write to the Free Software
021: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022: //
023: // Using this software in any meaning (reading, learning, copying, compiling,
024: // running) means that you agree that the Author(s) is (are) not responsible
025: // for cost, loss of data or any harm that may be caused directly or indirectly
026: // by usage of this softare or this documentation. The usage of this software
027: // is on your own risk. The installation and usage (starting/running) of this
028: // software may allow other people or application to access your computer and
029: // any attached devices and is highly dependent on the configuration of the
030: // software which must be done by the user of the software; the author(s) is
031: // (are) also not responsible for proper configuration and usage of the
032: // software, even if provoked by documentation provided together with
033: // the software.
034: //
035: // Any changes to this file according to the GPL as documented in the file
036: // gpl.txt aside this file in the shipment you received can be done to the
037: // lines that follows this copyright notice here, but changes must not be
038: // done inside the copyright notive above. A re-distribution must contain
039: // the intact and unchanged copyright notice.
040: // Contributions and changes to the program code must be marked as such.
041:
042: /*
043: */
044:
045: package de.anomic.kelondro;
046:
047: import java.io.File;
048: import java.io.IOException;
049: import java.util.HashMap;
050: import java.util.Iterator;
051:
052: public class kelondroDynTree {
053:
054: // basic data structures
055: private kelondroRow rowdef;
056: private kelondroDyn table;
057: private HashMap<String, kelondroRA> treeRAHandles;
058: private File file;
059:
060: // some properties to control caching and buffering
061: //private int maxcountCache = 1000, maxsizeCache = 100;
062: private int maxcountBuffer = 1000, maxsizeBuffer = 100;
063: //private long maxageCache = 60000, cycletimeCache = 10000;
064: private long maxageBuffer = 60000, cycletimeBuffer = 10000;
065: private long preloadTime = 0;
066:
067: // data structures for the cache and buffer
068: private HashMap<String, treeBuffer> buffer;
069: private HashMap<String, treeCache> cache;
070: private long cycleBuffer;
071:
072: public kelondroDynTree(File file, long preloadTime, int keylength,
073: int nodesize, kelondroRow rowdef, char fillChar,
074: boolean resetOnFail) {
075: // creates or opens a DynTree
076: this .file = file;
077: this .preloadTime = preloadTime;
078: this .rowdef = rowdef;
079: this .buffer = new HashMap<String, treeBuffer>();
080: this .cache = new HashMap<String, treeCache>();
081: //this.cycleCache = Long.MIN_VALUE;
082: this .cycleBuffer = Long.MIN_VALUE;
083: this .table = new kelondroDyn(file, true, true, preloadTime,
084: keylength, nodesize, fillChar, rowdef.objectOrder,
085: true, false, resetOnFail);
086: this .treeRAHandles = new HashMap<String, kelondroRA>();
087: }
088:
089: public void close() throws IOException {
090: Iterator<String> e = treeRAHandles.keySet().iterator();
091: while (e.hasNext())
092: closeTree((String) e.next());
093: int size = table.sizeDyn();
094: table.close();
095: if (size == 0)
096: this .file.delete();
097: }
098:
099: public void setWriteBufferAttr(int maxcount, int maxsize,
100: long maxage, long cycletime) {
101: maxcountBuffer = maxcount;
102: maxsizeBuffer = maxsize;
103: maxageBuffer = maxage;
104: cycletimeBuffer = cycletime;
105: }
106:
107: protected boolean existsTree(String key) throws IOException {
108: return table.existsDyn(key);
109: }
110:
111: protected kelondroTree newTree(String key) throws IOException {
112: if (table.existsDyn(key))
113: throw new IOException("table " + key + " already exists.");
114: kelondroRA ra = table.getRA(key); // works always, even with no-existing entry
115: treeRAHandles.put(key, ra);
116: try {
117: return new kelondroTree(ra, this .file.getCanonicalPath()
118: + "#" + key, true, preloadTime, rowdef, false);
119: } catch (RuntimeException e) {
120: throw new IOException(e.getMessage());
121: }
122: }
123:
124: protected kelondroTree getTree(String key) throws IOException {
125: if (table.existsDyn(key)) {
126: kelondroRA ra = table.getRA(key);
127: treeRAHandles.put(key, ra);
128: return new kelondroTree(ra, this .file.getCanonicalPath()
129: + "#" + key, true, preloadTime);
130: }
131: return null;
132: }
133:
134: protected void closeTree(String key) throws IOException {
135: kelondroRA ra = (kelondroRA) treeRAHandles.get(key);
136: if (ra != null) {
137: ra.close();
138: treeRAHandles.remove(key);
139: }
140: }
141:
142: protected void removeTree(String key) throws IOException {
143: kelondroRA ra = (kelondroRA) treeRAHandles.get(key);
144: if (ra != null) {
145: ra.close();
146: treeRAHandles.remove(key);
147: }
148: table.remove(key);
149: }
150:
151: /*******************************************************/
152:
153: protected class treeCache {
154:
155: private String tablename;
156: private HashMap<String, kelondroRow.Entry> tcache;
157: public long timestamp;
158:
159: treeCache(String tablename) {
160: this .tablename = tablename;
161: this .tcache = new HashMap<String, kelondroRow.Entry>(); // for key-row relations
162: this .timestamp = Long.MAX_VALUE; // to flag no-update
163: }
164:
165: public kelondroRow.Entry get(byte[] key) throws IOException {
166: kelondroRow.Entry entry = (kelondroRow.Entry) tcache
167: .get(new String(key));
168: if (entry == null) {
169: kelondroTree t = getTree(this .tablename);
170: entry = t.get(key);
171: t.close();
172: this .tcache.put(new String(key), entry);
173: this .timestamp = System.currentTimeMillis();
174: }
175: return entry;
176: }
177:
178: protected void put(kelondroRow.Entry entry) { // this is only used internal
179: this .tcache.put(entry.getColString(0, null), entry);
180: this .timestamp = System.currentTimeMillis();
181: }
182:
183: protected void remove(byte[] key) {
184: this .tcache.remove(new String(key));
185: this .timestamp = System.currentTimeMillis();
186: }
187: }
188:
189: protected class treeBuffer {
190:
191: private String tablename;
192: protected HashMap<String, kelondroRow.Entry> tbuffer;
193: public long timestamp;
194:
195: treeBuffer(String tablename) {
196: this .tablename = tablename;
197: this .tbuffer = new HashMap<String, kelondroRow.Entry>(); // for key-row relations
198: this .timestamp = Long.MAX_VALUE; // to flag no-update
199: }
200:
201: public void put(kelondroRow.Entry entry) {
202: this .tbuffer.put(entry.getColString(0, null), entry);
203: this .timestamp = System.currentTimeMillis();
204: }
205:
206: public void remove(byte[] key) {
207: this .tbuffer.remove(new String(key));
208: this .timestamp = System.currentTimeMillis();
209: }
210:
211: protected void flush() throws IOException {
212: this .timestamp = System.currentTimeMillis();
213: if (this .tbuffer.size() == 0)
214: return;
215: Iterator<String> e = this .tbuffer.keySet().iterator();
216: kelondroTree t = getTree(this .tablename);
217: kelondroRow.Entry entry;
218: String key;
219: while (e.hasNext()) {
220: key = e.next();
221: entry = (kelondroRow.Entry) this .tbuffer.get(key);
222: t.put(entry);
223: }
224: t.close();
225: }
226: }
227:
228: /*******************************************************/
229:
230: // read cached
231: public synchronized kelondroRow.Entry get(String tablename,
232: byte[] key) throws IOException {
233: treeCache tc = (treeCache) cache.get(table);
234: if (tc == null) {
235: tc = new treeCache(tablename);
236: cache.put(tablename, tc);
237: }
238: return tc.get(key);
239: }
240:
241: // write buffered
242: public synchronized void put(String tablename,
243: kelondroRow.Entry newrow) {
244: treeBuffer tb = (treeBuffer) buffer.get(tablename);
245: if (tb == null) {
246: tb = new treeBuffer(tablename);
247: }
248: treeCache tc = (treeCache) cache.get(table);
249: if (tc == null) {
250: tc = new treeCache(tablename);
251: cache.put(tablename, tc);
252: }
253: tb.put(newrow);
254: tc.put(newrow);
255: flushBuffer();
256: }
257:
258: public synchronized void remove(String tablename, byte[] key) {
259: treeBuffer tb = (treeBuffer) buffer.get(tablename);
260: if (tb == null) {
261: tb = new treeBuffer(tablename);
262: }
263: treeCache tc = (treeCache) cache.get(table);
264: if (tc == null) {
265: tc = new treeCache(tablename);
266: cache.put(tablename, tc);
267: }
268: tb.remove(key);
269: tc.remove(key);
270: flushBuffer();
271: }
272:
273: public synchronized void removeAll(String tablename)
274: throws IOException {
275: buffer.remove(table);
276: cache.remove(table);
277: kelondroTree t = getTree(tablename);
278: t.removeAll();
279: flushBuffer();
280: }
281:
282: // clean-up method for buffer:
283: private void flushBuffer() {
284: if ((System.currentTimeMillis() - this .cycleBuffer < this .cycletimeBuffer)
285: && (buffer.size() < this .maxcountBuffer))
286: return;
287: this .cycleBuffer = System.currentTimeMillis();
288: // collect all buffers which have a time > maxageBuffer
289: Iterator<String> e = buffer.keySet().iterator();
290: String tablename;
291: treeBuffer tb;
292: while (e.hasNext()) {
293: tablename = e.next();
294: tb = (treeBuffer) buffer.get(tablename);
295: if ((System.currentTimeMillis() - tb.timestamp > this .maxageBuffer)
296: || (tb.tbuffer.size() > this .maxsizeBuffer)
297: || (buffer.size() > this .maxcountBuffer)) {
298: try {
299: tb.flush();
300: } catch (IOException ee) {
301: }
302: tb = null;
303: buffer.remove(tablename);
304: }
305: }
306: }
307:
308: /*******************************************************/
309:
310: public static void main(String[] args) {
311: // test app
312: try {
313: System.out.println("start");
314: File file = new File("D:\\bin\\testDyn.db");
315: if (file.exists()) {
316: kelondroDynTree dt = new kelondroDynTree(
317: file,
318: 0,
319: 16,
320: 512,
321: new kelondroRow(
322: "byte[] a-10, byte[] b-20, byte[] c-30",
323: kelondroNaturalOrder.naturalOrder, 0),
324: '_', true);
325: System.out.println("opened: table keylength="
326: + dt.table.row().width(0) + ", sectorsize="
327: + dt.table.row().width(1) + ", "
328: + dt.table.sizeDyn() + " entries.");
329: } else {
330: kelondroDynTree dt = new kelondroDynTree(
331: file,
332: 0,
333: 16,
334: 512,
335: new kelondroRow(
336: "byte[] a-10, byte[] b-20, byte[] c-30",
337: kelondroNaturalOrder.naturalOrder, 0),
338: '_', true);
339: String name;
340: kelondroTree t;
341: kelondroRow.Entry line;
342: for (int i = 1; i < 100; i++) {
343: name = "test" + i;
344: t = dt.newTree(name);
345: line = t.row()
346: .newEntry(
347: new byte[][] { "".getBytes(),
348: "abc".getBytes(),
349: "def".getBytes() });
350: for (int j = 1; j < 10; j++) {
351: line.setCol(0, ("entry" + j).getBytes());
352: t.put(line);
353: }
354: dt.closeTree(name);
355: }
356: }
357: System.out.println("finished");
358: } catch (IOException e) {
359: e.printStackTrace();
360: }
361: }
362:
363: }
|