package org.aspectj.weaver.tools.cache;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StreamCorruptedException;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.aspectj.util.FileUtil;
import org.aspectj.util.LangUtil;
public class ZippedFileCacheBacking extends AsynchronousFileCacheBacking {
public static final String ZIP_FILE = "cache.zip";
private static final AsynchronousFileCacheBackingCreator<ZippedFileCacheBacking> defaultCreator=
new AsynchronousFileCacheBackingCreator<ZippedFileCacheBacking>() {
public ZippedFileCacheBacking create(File cacheDir) {
return new ZippedFileCacheBacking(cacheDir);
}
};
private final File zipFile;
public ZippedFileCacheBacking(File cacheDir) {
super(cacheDir);
zipFile = new File(cacheDir, ZIP_FILE);
}
public File getZipFile () {
return zipFile;
}
public static final ZippedFileCacheBacking createBacking (File cacheDir) {
return createBacking(cacheDir, defaultCreator);
}
@Override
protected void writeClassBytes(String key, byte[] bytes) throws Exception {
File outFile=getZipFile();
Map<String,byte[]> entriesMap;
try {
entriesMap = readZipClassBytes(outFile);
} catch(Exception e) {
if ((logger != null) && logger.isTraceEnabled()) {
logger.warn("writeClassBytes(" + outFile + ")[" + key + "]"
+ " failed (" + e.getClass().getSimpleName() + ")"
+ " to read current data: " + e.getMessage(),
e);
}
FileUtil.deleteContents(outFile);
return;
}
if (entriesMap.isEmpty()) {
entriesMap = Collections.singletonMap(key, bytes);
} else {
entriesMap.put(key, bytes);
}
try {
writeZipClassBytes(outFile, entriesMap);
} catch(Exception e) {
if ((logger != null) && logger.isTraceEnabled()) {
logger.warn("writeClassBytes(" + outFile + ")[" + key + "]"
+ " failed (" + e.getClass().getSimpleName() + ")"
+ " to write updated data: " + e.getMessage(),
e);
}
FileUtil.deleteContents(outFile);
}
}
@Override
protected void removeClassBytes(String key) throws Exception {
File outFile=getZipFile();
Map<String,byte[]> entriesMap;
try {
entriesMap = readZipClassBytes(outFile);
} catch(Exception e) {
if ((logger != null) && logger.isTraceEnabled()) {
logger.warn("removeClassBytes(" + outFile + ")[" + key + "]"
+ " failed (" + e.getClass().getSimpleName() + ")"
+ " to read current data: " + e.getMessage(),
e);
}
FileUtil.deleteContents(outFile);
return;
}
if (!entriesMap.isEmpty()) {
if (entriesMap.remove(key) == null) {
return;
}
}
try {
writeZipClassBytes(outFile, entriesMap);
} catch(Exception e) {
if ((logger != null) && logger.isTraceEnabled()) {
logger.warn("removeClassBytes(" + outFile + ")[" + key + "]"
+ " failed (" + e.getClass().getSimpleName() + ")"
+ " to write updated data: " + e.getMessage(),
e);
}
FileUtil.deleteContents(outFile);
}
}
@Override
protected Map<String, byte[]> readClassBytes(Map<String, IndexEntry> indexMap, File cacheDir) {
File dataFile=new File(cacheDir, ZIP_FILE);
Map<String,byte[]> entriesMap;
boolean okEntries=true;
try {
entriesMap = readZipClassBytes(dataFile);
} catch(Exception e) {
if ((logger != null) && logger.isTraceEnabled()) {
logger.warn("Failed (" + e.getClass().getSimpleName() + ")"
+ " to read zip entries from " + dataFile
+ ": " + e.getMessage(),
e);
}
entriesMap = new TreeMap<String,byte[]>();
okEntries = false;
}
if (!syncClassBytesEntries(dataFile, indexMap, entriesMap)) {
okEntries = false;
}
if (!okEntries) {
FileUtil.deleteContents(dataFile);
if (!entriesMap.isEmpty()) {
entriesMap.clear();
}
}
syncIndexEntries(dataFile, indexMap, entriesMap);
return entriesMap;
}
protected Collection<String> syncIndexEntries (File dataFile, Map<String, IndexEntry> indexMap, Map<String,byte[]> entriesMap) {
Collection<String> toDelete=null;
for (Map.Entry<String, IndexEntry> ie : indexMap.entrySet()) {
String key=ie.getKey();
IndexEntry indexEntry=ie.getValue();
if (indexEntry.ignored) {
continue;
}
if (entriesMap.containsKey(key)) {
continue;
}
if ((logger != null) && logger.isTraceEnabled()) {
logger.debug("syncIndexEntries(" + dataFile + ")[" + key + "] no class bytes");
}
if (toDelete == null) {
toDelete = new TreeSet<String>();
}
toDelete.add(key);
}
if (toDelete == null) {
return Collections.emptySet();
}
for (String key : toDelete) {
indexMap.remove(key);
}
return toDelete;
}
protected boolean syncClassBytesEntries (File dataFile, Map<String, IndexEntry> indexMap, Map<String,byte[]> entriesMap) {
boolean okEntries=true;
for (Map.Entry<String,byte[]> bytesEntry : entriesMap.entrySet()) {
String key=bytesEntry.getKey();
IndexEntry indexEntry=indexMap.get(key);
if ((indexEntry == null) || indexEntry.ignored) {
if ((logger != null) && logger.isTraceEnabled()) {
logger.debug("syncClassBytesEntries(" + dataFile + ")[" + key + "] bad index entry");
}
okEntries = false;
continue;
}
long crc=crc(bytesEntry.getValue());
if (crc != indexEntry.crcWeaved) {
if ((logger != null) && logger.isTraceEnabled()) {
logger.debug("syncClassBytesEntries(" + dataFile + ")[" + key + "]"
+ " mismatched CRC - expected=" + indexEntry.crcWeaved + "/got=" + crc);
}
indexMap.remove(key);
okEntries = false;
continue;
}
}
return okEntries;
}
@Override
protected IndexEntry resolveIndexMapEntry(File cacheDir, IndexEntry ie) {
if (cacheDir.exists()) {
return ie;
} else {
return null;
}
}
public static final Map<String,byte[]> readZipClassBytes (File file) throws IOException {
if (!file.canRead()) {
return Collections.emptyMap();
}
Map<String,byte[]> result=new TreeMap<String,byte[]>();
byte[] copyBuf=new byte[4096];
ByteArrayOutputStream out=new ByteArrayOutputStream(copyBuf.length);
ZipFile zipFile=new ZipFile(file);
try {
for (Enumeration<? extends ZipEntry> entries=zipFile.entries(); (entries != null) && entries.hasMoreElements(); ) {
ZipEntry e=entries.nextElement();
String name=e.getName();
if (LangUtil.isEmpty(name)) {
continue;
}
out.reset();
InputStream zipStream=zipFile.getInputStream(e);
try {
for (int nRead=zipStream.read(copyBuf); nRead != (-1); nRead=zipStream.read(copyBuf)) {
out.write(copyBuf, 0, nRead);
}
} finally {
zipStream.close();
}
byte[] data=out.toByteArray(), prev=result.put(name, data);
if (prev != null) {
throw new StreamCorruptedException("Multiple entries for " + name);
}
}
} finally {
zipFile.close();
}
return result;
}
public static final void writeZipClassBytes (File file, Map<String,byte[]> entriesMap) throws IOException {
if (entriesMap.isEmpty()) {
FileUtil.deleteContents(file);
return;
}
File zipDir=file.getParentFile();
if ((!zipDir.exists()) && (!zipDir.mkdirs())) {
throw new IOException("Failed to create path to " + zipDir.getAbsolutePath());
}
ZipOutputStream zipOut=new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(file), 4096));
try {
for (Map.Entry<String,byte[]> bytesEntry : entriesMap.entrySet()) {
String key=bytesEntry.getKey();
byte[] bytes=bytesEntry.getValue();
zipOut.putNextEntry(new ZipEntry(key));
zipOut.write(bytes);
zipOut.closeEntry();
}
} finally {
zipOut.close();
}
}
}