/* * JBoss, Home of Professional Open Source. * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ package org.teiid.common.buffer.impl; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.teiid.common.buffer.FileStore; import org.teiid.common.buffer.StorageManager; import org.teiid.core.TeiidComponentException; /** * A storage manager that combines smaller files into a larger * logical file. * * The buffer methods assume that buffers cannot go beyond single * file boundaries. */ public class SplittableStorageManager implements StorageManager { public static final long DEFAULT_MAX_FILESIZE = 2 * 1024l; private long maxFileSize = DEFAULT_MAX_FILESIZE * 1024l * 1024l; // 2GB private StorageManager storageManager; public SplittableStorageManager(StorageManager storageManager) { this.storageManager = storageManager; } @Override public FileStore createFileStore(String name) { return new SplittableFileStore(name); } @Override public void initialize() throws TeiidComponentException { storageManager.initialize(); } public class SplittableFileStore extends FileStore { private String name; private List<FileStore> storageFiles = new ArrayList<FileStore>(); private volatile long len; public SplittableFileStore(String name) { this.name = name; } @Override public long getLength() { return len; } @Override protected int readWrite(long fileOffset, byte[] b, int offSet, int length, boolean write) throws IOException { FileStore store = null; if (!write) { synchronized (this) { if (fileOffset > len) { throw new IOException("Invalid file position " + fileOffset + " length " + length); //$NON-NLS-1$ //$NON-NLS-2$ } store = storageFiles.get((int)(fileOffset/maxFileSize)); } return store.read(fileOffset%maxFileSize, b, offSet, length); } synchronized (this) { ensureLength(fileOffset + length); store = storageFiles.get((int)(fileOffset/maxFileSize)); } long fileBegin = fileOffset%maxFileSize; length = Math.min(length, (int)Math.min(Integer.MAX_VALUE, maxFileSize - fileBegin)); store.write(fileBegin, b, offSet, length); return length; } private void ensureLength(long length) throws IOException { if (length <= len) { return; } int numFiles = (int)(length/maxFileSize); long lastFileSize = length%maxFileSize; if (lastFileSize > 0) { numFiles++; } for (int i = storageFiles.size(); i < numFiles; i++) { FileStore newFileInfo = storageManager.createFileStore(name + "_" + storageFiles.size()); //$NON-NLS-1$ storageFiles.add(newFileInfo); if (lastFileSize == 0 || i != numFiles - 1) { newFileInfo.setLength(maxFileSize); } } if (lastFileSize > 0) { storageFiles.get(storageFiles.size() - 1).setLength(lastFileSize); } len = length; } @Override public synchronized void setLength(long length) throws IOException { if (length > len) { ensureLength(length); } else { int numFiles = (int)(length/maxFileSize); long lastFileSize = length%maxFileSize; if (lastFileSize > 0) { numFiles++; } int toRemove = storageFiles.size() - numFiles; for (int i = 0; i < toRemove; i++) { FileStore store = storageFiles.remove(storageFiles.size() -1); store.remove(); } if (lastFileSize > 0) { storageFiles.get(storageFiles.size() - 1).setLength(lastFileSize); } } len = length; } public synchronized void removeDirect() { for (int i = storageFiles.size() - 1; i >= 0; i--) { this.storageFiles.remove(i).remove(); } } } public long getMaxFileSize() { return maxFileSize; } public void setMaxFileSize(long maxFileSize) { this.maxFileSize = maxFileSize * 1024l * 1024l; } public void setMaxFileSizeDirect(long maxFileSize) { this.maxFileSize = maxFileSize; } public StorageManager getStorageManager() { return storageManager; } @Override public long getMaxStorageSpace() { return storageManager.getMaxStorageSpace(); } }