org.aspectj.weaver.tools.cache.AbstractIndexedFileCacheBacking Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2012 Contributors.
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v 2.0
* which accompanies this distribution and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
*
* Contributors:
* John Kew (vmware) initial implementation
* Lyor Goldstein (vmware) add support for weaved class being re-defined
*******************************************************************************/
package org.aspectj.weaver.tools.cache;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.StreamCorruptedException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;
import org.aspectj.util.LangUtil;
/**
* Uses an index
file to keep track of the cached entries
*/
public abstract class AbstractIndexedFileCacheBacking extends AbstractFileCacheBacking {
/**
* Default name of cache index file - assumed to contain {@link IndexEntry}-s
*/
public static final String INDEX_FILE = "cache.idx";
protected static final IndexEntry[] EMPTY_INDEX=new IndexEntry[0];
protected static final String[] EMPTY_KEYS=new String[0];
private final File indexFile;
protected AbstractIndexedFileCacheBacking(File cacheDir) {
super(cacheDir);
indexFile = new File(cacheDir, INDEX_FILE);
}
public File getIndexFile () {
return indexFile;
}
public String[] getKeys(String regex) {
Map index=getIndex();
if ((index == null) || index.isEmpty()) {
return EMPTY_KEYS;
}
Collection matches= new LinkedList<>();
synchronized(index) {
for (String key : index.keySet()) {
if (key.matches(regex)) {
matches.add(key);
}
}
}
if (matches.isEmpty()) {
return EMPTY_KEYS;
} else {
return matches.toArray(new String[0]);
}
}
protected Map readIndex () {
return readIndex(getCacheDirectory(), getIndexFile());
}
protected void writeIndex () {
writeIndex(getIndexFile());
}
protected void writeIndex (File file) {
try {
writeIndex(file, getIndex());
} catch(Exception e) {
if ((logger != null) && logger.isTraceEnabled()) {
logger.warn("writeIndex(" + file + ") " + e.getClass().getSimpleName() + ": " + e.getMessage(), e);
}
}
}
protected abstract Map getIndex ();
protected Map readIndex (File cacheDir, File cacheFile) {
Map indexMap= new TreeMap<>();
IndexEntry[] idxValues=readIndex(cacheFile);
if (LangUtil.isEmpty(idxValues)) {
if ((logger != null) && logger.isTraceEnabled()) {
logger.debug("readIndex(" + cacheFile + ") no index entries");
}
return indexMap;
}
for (IndexEntry ie : idxValues) {
IndexEntry resEntry=resolveIndexMapEntry(cacheDir, ie);
if (resEntry != null) {
indexMap.put(resEntry.key, resEntry);
} else if ((logger != null) && logger.isTraceEnabled()) {
logger.debug("readIndex(" + cacheFile + ") skip " + ie.key);
}
}
return indexMap;
}
protected IndexEntry resolveIndexMapEntry (File cacheDir, IndexEntry ie) {
return ie;
}
public IndexEntry[] readIndex(File indexFile) {
if (!indexFile.canRead()) {
return EMPTY_INDEX;
}
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream(indexFile));
return (IndexEntry[]) ois.readObject();
} catch (Exception e) {
if ((logger != null) && logger.isTraceEnabled()) {
logger.error("Failed (" + e.getClass().getSimpleName() + ")"
+ " to read index from " + indexFile.getAbsolutePath()
+ " : " + e.getMessage(), e);
}
delete(indexFile);
} finally {
close(ois, indexFile);
}
return EMPTY_INDEX;
}
protected void writeIndex (File indexFile, Map index) throws IOException {
writeIndex(indexFile, LangUtil.isEmpty(index) ? Collections.emptyList() : index.values());
}
protected void writeIndex (File indexFile, IndexEntry ... entries) throws IOException {
writeIndex(indexFile, LangUtil.isEmpty(entries) ? Collections.emptyList() : Arrays.asList(entries));
}
protected void writeIndex (File indexFile, Collection extends IndexEntry> entries) throws IOException {
File indexDir=indexFile.getParentFile();
if ((!indexDir.exists()) && (!indexDir.mkdirs())) {
throw new IOException("Failed to create path to " + indexFile.getAbsolutePath());
}
int numEntries=LangUtil.isEmpty(entries) ? 0 : entries.size();
IndexEntry[] entryValues=(numEntries <= 0) ? null : entries.toArray(new IndexEntry[numEntries]);
// if no entries, simply delete the index file
if (LangUtil.isEmpty(entryValues)) {
if (indexFile.exists() && (!indexFile.delete())) {
throw new StreamCorruptedException("Failed to clean up index file at " + indexFile.getAbsolutePath());
}
return;
}
ObjectOutputStream oos=new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(indexFile), 4096));
try {
oos.writeObject(entryValues);
} finally {
close(oos, indexFile);
}
}
public static final IndexEntry createIndexEntry (CachedClassEntry classEntry, byte[] originalBytes) {
if (classEntry == null) {
return null;
}
IndexEntry indexEntry = new IndexEntry();
indexEntry.key = classEntry.getKey();
indexEntry.generated = classEntry.isGenerated();
indexEntry.ignored = classEntry.isIgnored();
indexEntry.crcClass = crc(originalBytes);
if (!classEntry.isIgnored()) {
indexEntry.crcWeaved = crc(classEntry.getBytes());
}
return indexEntry;
}
/**
* The default index entry in the index file
*/
public static class IndexEntry implements Serializable, Cloneable {
private static final long serialVersionUID = 756391290557029363L;
public String key;
public boolean generated;
public boolean ignored;
public long crcClass;
public long crcWeaved;
public IndexEntry () {
super();
}
@Override
public IndexEntry clone () {
try {
return getClass().cast(super.clone());
} catch(CloneNotSupportedException e) {
throw new RuntimeException("Failed to clone: " + toString() + ": " + e.getMessage(), e);
}
}
@Override
public int hashCode() {
return (int) (key.hashCode()
+ (generated ? 1 : 0)
+ (ignored ? 1 : 0)
+ crcClass
+ crcWeaved);
}
@Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (this == obj)
return true;
if (getClass() != obj.getClass())
return false;
IndexEntry other=(IndexEntry) obj;
if (this.key.equals(other.key)
&& (this.ignored == other.ignored)
&& (this.generated == other.generated)
&& (this.crcClass == other.crcClass)
&& (this.crcWeaved == other.crcWeaved)) {
return true;
} else {
return false;
}
}
@Override
public String toString() {
return key
+ "[" + (generated ? "generated" : "ignored") + "]"
+ ";crcClass=0x" + Long.toHexString(crcClass)
+ ";crcWeaved=0x" + Long.toHexString(crcWeaved)
;
}
}
}