Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
/**
* A nifty class that lets you squirrel away data on the file system. Write
* once, read many times. Instance of this are thread-safe by way of internal
* synchronization.
*
* Note that in the current implementation, the backing temp file will get
* arbitrarily large as you continue adding things to it. There is no internal
* GC or compaction.
*/
public class DiskCache {
/**
* For future thought: if we used Object tokens instead of longs, we could
* actually track references and do GC/compaction on the underlying file.
*
* I considered using memory mapping, but I didn't see any obvious way to make
* the map larger after the fact, which kind of defeats the infinite-append
* design. At any rate, I measured the current performance of this design to
* be so fast relative to what I'm using it for, I didn't pursue this further.
*/
/**
* A global shared Disk cache.
*/
public static DiskCache INSTANCE = new DiskCache();
private boolean atEnd = true;
private final RandomAccessFile file;
private DiskCache() {
try {
File temp = File.createTempFile("gwt", "byte-cache");
temp.deleteOnExit();
file = new RandomAccessFile(temp, "rw");
file.setLength(0);
registerShutdownHook();
} catch (IOException e) {
throw new RuntimeException("Unable to initialize byte cache", e);
}
}
/**
* Retrieve the underlying bytes.
*
* @param token a previously returned token
* @return the bytes that were written
*/
public synchronized byte[] readByteArray(long token) {
try {
atEnd = false;
file.seek(token);
int length = file.readInt();
byte[] result = new byte[length];
file.readFully(result);
return result;
} catch (IOException e) {
throw new RuntimeException("Unable to read from byte cache", e);
}
}
/**
* Deserialize the underlying bytes as an object.
*
* @param the type of the object to deserialize
* @param token a previously returned token
* @param type the type of the object to deserialize
* @return the deserialized object
*/
public T readObject(long token, Class type) {
try {
byte[] bytes = readByteArray(token);
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
return Util.readStreamAsObject(in, type);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Unexpected exception deserializing from disk cache", e);
} catch (IOException e) {
throw new RuntimeException("Unexpected exception deserializing from disk cache", e);
}
}
/**
* Read the underlying bytes as a String.
*
* @param token a previously returned token
* @return the String that was written
*/
public String readString(long token) {
return Util.toString(readByteArray(token));
}
/**
* Write the rest of the data in an input stream to disk. Note: this method
* does not close the InputStream.
*
* @param in open stream containing the data to write to the disk cache.
*
* @return a token to retrieve the data later
*/
public synchronized long transferFromStream(InputStream in) throws IOException {
assert in != null;
byte[] buf = Util.takeThreadLocalBuf();
try {
long position = moveToEndPosition();
// Placeholder, we don't know the length yet.
file.writeInt(-1);
// Transfer all the bytes.
int length = 0;
int bytesRead;
while ((bytesRead = in.read(buf)) != -1) {
file.write(buf, 0, bytesRead);
length += bytesRead;
}
// Now go back and fill in the length.
file.seek(position);
file.writeInt(length);
// Don't eagerly seek the end, the next operation might be a read.
atEnd = false;
return position;
} finally {
Util.releaseThreadLocalBuf(buf);
}
}
/**
* Writes the underlying bytes into the specified output stream.
*
* @param token a previously returned token
* @param out the stream to write into
*/
public synchronized void transferToStream(long token, OutputStream out) throws IOException {
byte[] buf = Util.takeThreadLocalBuf();
try {
atEnd = false;
file.seek(token);
int length = file.readInt();
int bufLen = buf.length;
while (length > bufLen) {
int read = file.read(buf, 0, bufLen);
length -= read;
out.write(buf, 0, read);
}
while (length > 0) {
int read = file.read(buf, 0, length);
length -= read;
out.write(buf, 0, read);
}
} finally {
Util.releaseThreadLocalBuf(buf);
}
}
/**
* Write a byte array to disk.
*
* @return a token to retrieve the data later
*/
public synchronized long writeByteArray(byte[] bytes) {
try {
long position = moveToEndPosition();
file.writeInt(bytes.length);
file.write(bytes);
return position;
} catch (IOException e) {
throw new RuntimeException("Unable to write to byte cache", e);
}
}
/**
* Serialize an Object to disk.
*
* @return a token to retrieve the data later
*/
public long writeObject(Object object) {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Util.writeObjectToStream(out, object);
return writeByteArray(out.toByteArray());
} catch (IOException e) {
throw new RuntimeException("Unexpected IOException on in-memory stream", e);
}
}
/**
* Write a String to disk as bytes.
*
* @return a token to retrieve the data later
*/
public long writeString(String str) {
return writeByteArray(Util.getBytes(str));
}
/**
* Moves to the end of the file if necessary and returns the offset position.
* Caller must synchronize.
*
* @return the offset position of the end of the file
* @throws IOException
*/
private long moveToEndPosition() throws IOException {
// Get an end pointer.
if (atEnd) {
return file.getFilePointer();
} else {
long position = file.length();
file.seek(position);
atEnd = true;
return position;
}
}
/**
* Register a shutdown hook to close the RandomAccessFile associated with the temp file.
* There is a known bug
* in Windows that prevents the 'temp' file from being deleted by 'deleteOnExit'
* (see {@link DiskCache#DiskCache()}) because it is still open by the RandomAccessFile.
* This hook forces the RandomAccessFile to be closed at shutdown to allow the correct
* 'temp' file removal.
*/
private void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
try {
file.close();
} catch (IOException e) {
// No exception handling in a shutdown hook
}
}
}));
}
}