blob: 3c7c6acab53d091359cf4867d1ccad2ab4464e67 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2009 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.team.internal.core;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.jobs.ILock;
import org.eclipse.osgi.util.NLS;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.variants.CachedResourceVariant;
/**
* This class provides the implementation for the ICacheEntry
*/
public class ResourceVariantCacheEntry {
public static final int UNINITIALIZED = 0;
public static final int READY = 1;
public static final int DISPOSED = 2;
private String id;
private String filePath;
private ResourceVariantCache cache;
private int state = UNINITIALIZED;
private long lastAccess;
private CachedResourceVariant resourceVariant;
private ILock lock;
public ResourceVariantCacheEntry(ResourceVariantCache cache, ILock lock, String id, String filePath) {
this.lock = lock;
state = UNINITIALIZED;
this.cache = cache;
this.id = id;
this.filePath = filePath;
registerHit();
}
/* (non-Javadoc)
* @see org.eclipse.team.core.sync.ICacheEntry#getContents()
*/
public InputStream getContents() throws TeamException {
if (state != READY) return null;
registerHit();
File ioFile = getFile();
try {
try {
if (ioFile.exists()) {
return new FileInputStream(ioFile);
}
} catch (IOException e) {
// Try to purge the cache and continue
cache.purgeFromCache(this);
throw e;
}
} catch (IOException e) {
// We will end up here if we couldn't read or delete the cache file
throw new TeamException(NLS.bind(Messages.RemoteContentsCache_fileError, new String[] { ioFile.getAbsolutePath() }), e);
}
// This can occur when there is no remote contents
return new ByteArrayInputStream(new byte[0]);
}
protected File getFile() {
return new File(cache.getCachePath().toFile(), filePath);
}
/**
* Set the contents of for this cache entry. This method supports concurrency by only allowing
* one cache entry to be written at a time. In the case of two concurrent writes to the same cache entry,
* the contents from the first write is used and the content from subsequent writes is ignored.
* @param stream an InputStream that provides the contents to be cached
* @param monitor a progress monitor
* @throws TeamException if the entry is DISPOSED or an I/O error occurred
*/
public void setContents(InputStream stream, IProgressMonitor monitor) throws TeamException {
// Use a lock to only allow one write at a time
beginOperation();
try {
internalSetContents(stream, monitor);
} finally {
endOperation();
}
}
private void endOperation() {
lock.release();
}
private void beginOperation() {
lock.acquire();
}
private void internalSetContents(InputStream stream, IProgressMonitor monitor) throws TeamException {
// if the state is DISPOSED then there is a problem
if (state == DISPOSED) {
throw new TeamException(NLS.bind(Messages.RemoteContentsCacheEntry_3, new String[] { cache.getName(), id }));
}
// Otherwise, the state is UNINITIALIZED or READY so we can proceed
registerHit();
File ioFile = getFile();
try {
// Open the cache file for writing
OutputStream out;
try {
if (state == UNINITIALIZED) {
out = new BufferedOutputStream(new FileOutputStream(ioFile));
} else {
// If the entry is READY, the contents must have been read in another thread.
// We still need to red the contents but they can be ignored since presumably they are the same
out = new ByteArrayOutputStream();
}
} catch (FileNotFoundException e) {
throw new TeamException(NLS.bind(Messages.RemoteContentsCache_fileError, new String[] { ioFile.getAbsolutePath() }), e);
}
// Transfer the contents
try {
try {
byte[] buffer = new byte[1024];
int read;
while ((read = stream.read(buffer)) >= 0) {
Policy.checkCanceled(monitor);
out.write(buffer, 0, read);
}
} finally {
out.close();
}
} catch (IOException e) {
// Make sure we don't leave the cache file around as it may not have the right contents
cache.purgeFromCache(this);
throw e;
}
// Mark the cache entry as ready
state = READY;
} catch (IOException e) {
throw new TeamException(NLS.bind(Messages.RemoteContentsCache_fileError, new String[] { ioFile.getAbsolutePath() }), e);
} finally {
try {
stream.close();
} catch (IOException e1) {
// Ignore close errors
}
}
}
/* (non-Javadoc)
* @see org.eclipse.team.core.sync.ICacheEntry#getState()
*/
public int getState() {
return state;
}
/* (non-Javadoc)
* @see org.eclipse.team.core.sync.ICacheEntry#getSize()
*/
public long getSize() {
if (state != READY) return 0;
File ioFile = getFile();
if (ioFile.exists()) {
return ioFile.length();
}
return 0;
}
/* (non-Javadoc)
* @see org.eclipse.team.core.sync.ICacheEntry#getLastAccessTimeStamp()
*/
public long getLastAccessTimeStamp() {
return lastAccess;
}
/**
* Registers a hit on this cache entry. This updates the last access timestamp.
* This method is intended to only be invoked from inside this class or the cache itself.
* Other clients should not use it.
*/
protected void registerHit() {
lastAccess = new Date().getTime();
}
public void dispose() {
// Use a lock to avoid changing state while another thread may be writing
beginOperation();
try {
state = DISPOSED;
cache.purgeFromCache(this);
} finally {
endOperation();
}
}
public String getId() {
return id;
}
public CachedResourceVariant getResourceVariant() {
return resourceVariant;
}
public void setResourceVariant(CachedResourceVariant resourceVariant) {
this.resourceVariant = resourceVariant;
}
}