001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package com.db4o.util.io;
022:
023: import java.io.*;
024: import java.lang.reflect.*;
025: import java.nio.*;
026: import java.nio.channels.*;
027: import java.util.*;
028:
029: import sun.nio.ch.*;
030:
031: import com.db4o.*;
032: import com.db4o.io.*;
033:
034: public class NIOFileAdapter extends IoAdapter {
035: private int hits = 0;
036: private int misses = 0;
037:
038: private int _pageSize;
039: private FileChannel _channel;
040: private MappedByteBuffer _page;
041: private int _pageId;
042: private long _position;
043: private long _size;
044: private boolean _dirty;
045: private Map _id2Page;
046: private LinkedList _lruPages;
047: private int _lruLimit;
048: private RandomAccessFile _file;
049:
050: public NIOFileAdapter(int pageSize, int lruLimit) {
051: _pageSize = pageSize;
052: _lruLimit = lruLimit;
053: }
054:
055: private NIOFileAdapter(String filename, boolean lockFile,
056: long initialLength, boolean readOnly, int pageSize,
057: int lruLimit) throws Db4oIOException {
058: _pageSize = pageSize;
059: try {
060: _file = new RandomAccessFile(filename, readOnly ? "r"
061: : "rw");
062: _channel = _file.getChannel();
063: _size = _channel.size();
064: } catch (IOException e) {
065: throw new Db4oIOException(e);
066: }
067: _page = null;
068: _pageId = 0;
069: _position = 0;
070: _dirty = false;
071: _id2Page = new HashMap();
072: _lruPages = new LinkedList();
073: _lruLimit = lruLimit;
074: }
075:
076: public void seek(long position) throws Db4oIOException {
077: _position = position;
078: }
079:
080: public void close() throws Db4oIOException {
081: for (Iterator pageiter = _id2Page.values().iterator(); pageiter
082: .hasNext();) {
083: MappedByteBuffer curpage = (MappedByteBuffer) pageiter
084: .next();
085: closePage(curpage);
086: }
087: _id2Page.clear();
088: _lruPages.clear();
089: _page = null;
090: try {
091: _channel.close();
092: _file.close();
093: } catch (IOException e) {
094: throw new Db4oIOException(e);
095: }
096: // System.err.println("Hits: "+hits+", Misses: "+misses);
097: }
098:
099: public void delete(String path) {
100: new File(path).delete();
101: }
102:
103: public boolean exists(String path) {
104: File existingFile = new File(path);
105: return existingFile.exists() && existingFile.length() > 0;
106: }
107:
108: public long getLength() throws Db4oIOException {
109: return _size;
110: }
111:
112: public int read(byte[] bytes, int length) throws Db4oIOException {
113: if (length <= 0) {
114: return 0;
115: }
116: int alreadyRead = 0;
117: int stillToRead = length;
118: while (stillToRead > 0) {
119: forcePage();
120: try {
121: _page.position(pageOffset(_position));
122: } catch (IllegalArgumentException exc) {
123: return -1;
124: }
125: int hereToRead = (int) min(
126: _page.limit() - _page.position(), stillToRead);
127: if (hereToRead == 0) {
128: break;
129: }
130: _page.get(bytes, alreadyRead, hereToRead);
131: stillToRead -= hereToRead;
132: alreadyRead += hereToRead;
133: _position += hereToRead;
134: }
135: return (alreadyRead > 0 ? alreadyRead : -1);
136: }
137:
138: public void write(byte[] bytes, int length) throws Db4oIOException {
139: if (length <= 0) {
140: return;
141: }
142: if (_position + length > _size) {
143: _size = _position + length;
144: }
145: int alreadyWritten = 0;
146: int stillToWrite = length;
147: while (stillToWrite > 0) {
148: forcePage();
149: int pageOffset = pageOffset(_position);
150: _page.limit((int) min(_pageSize, pageOffset + length));
151: _page.position(pageOffset);
152: int hereToWrite = (int) min(_page.capacity()
153: - _page.position(), stillToWrite);
154: _page.put(bytes, alreadyWritten, hereToWrite);
155: stillToWrite -= hereToWrite;
156: alreadyWritten += hereToWrite;
157: _position += hereToWrite;
158: _dirty = true;
159: }
160: }
161:
162: public void sync() throws Db4oIOException {
163: // FIXME internalSync(_page);
164: }
165:
166: private void internalSync(MappedByteBuffer page) {
167: if (_dirty && page != null) {
168: page.flip();
169: page.force();
170: }
171: }
172:
173: public void unlock() {
174: }
175:
176: public void lock() {
177: }
178:
179: private MappedByteBuffer page(int pageId) throws Db4oIOException {
180: MappedByteBuffer page;
181: try {
182: page = _channel.map(FileChannel.MapMode.READ_WRITE,
183: pagePosition(pageId), _pageSize);
184: } catch (IOException e) {
185: throw new Db4oIOException(e);
186: }
187: page.limit(pageSize(pageId));
188: return page;
189: }
190:
191: private long pagePosition(int pageId) {
192: return (long) pageId * _pageSize;
193: }
194:
195: private int pageSize(int pageId) throws Db4oIOException {
196: long sizeLeft = _size - pagePosition(pageId);
197: return (sizeLeft > _pageSize ? _pageSize : (int) sizeLeft);
198: }
199:
200: private void forcePage() throws Db4oIOException {
201: int pageId = pageId(_position);
202: int pageOffset = pageOffset(_position);
203: Integer pageIdKey = new Integer(pageId);
204: if (_id2Page.containsKey(pageIdKey)) {
205: _lruPages.remove(pageIdKey);
206: _lruPages.addFirst(pageIdKey);
207: _page = (MappedByteBuffer) _id2Page.get(pageIdKey);
208: _page.limit(pageSize(pageId));
209: hits++;
210: return;
211: }
212: closePage();
213: loadPage(pageId, pageOffset);
214: if (_lruPages.size() == _lruLimit) {
215: Integer dropPageKey = (Integer) _lruPages.removeLast();
216: MappedByteBuffer page = (MappedByteBuffer) _id2Page
217: .remove(dropPageKey);
218: try {
219: Method unmap = FileChannelImpl.class
220: .getDeclaredMethod("unmap",
221: new Class[] { MappedByteBuffer.class });
222: unmap.setAccessible(true);
223: unmap.invoke(null, new Object[] { page });
224: } catch (Exception exc) {
225: exc.printStackTrace();
226: }
227: }
228: Integer addedPageKey = new Integer(_pageId);
229: _id2Page.put(addedPageKey, _page);
230: _lruPages.addFirst(addedPageKey);
231: _dirty = false;
232: misses++;
233: }
234:
235: private void closePage() throws Db4oIOException {
236: closePage(_page);
237: _page = null;
238: }
239:
240: private void closePage(MappedByteBuffer page)
241: throws Db4oIOException {
242: if (page != null) {
243: internalSync(page);
244: }
245: }
246:
247: private void loadPage(int pageId, int pageOffset)
248: throws Db4oIOException {
249: _page = page(pageId);
250: _pageId = pageId;
251: _dirty = false;
252: }
253:
254: private int pageId(long position) {
255: return (int) (position / _pageSize);
256: }
257:
258: private int pageOffset(long position) {
259: return (int) (position % _pageSize);
260: }
261:
262: private long min(long a, long b) {
263: return (a > b ? b : a);
264: }
265:
266: public IoAdapter open(String path, boolean lockFile,
267: long initialLength, boolean readOnly)
268: throws Db4oIOException {
269: return new NIOFileAdapter(path, lockFile, initialLength,
270: readOnly, _pageSize, _lruLimit);
271: }
272: }
|