blob: 11bbcb0b0122dfc428d71ef1124525df968291b8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 The Eclipse Foundation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* The Eclipse Foundation - initial API and implementation
* Yatta Solutions - bug 413871: performance
*******************************************************************************/
package org.eclipse.epp.internal.mpc.ui.catalog;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.epp.internal.mpc.core.util.TransportFactory;
import org.eclipse.epp.internal.mpc.core.util.URLUtil;
import org.eclipse.epp.internal.mpc.ui.MarketplaceClientUi;
/**
* @author David Green
* @author Carsten Reckord
*/
public class ResourceProvider {
public static interface ResourceReceiver<T> {
T processResource(URL resource);
void setResource(T resource);
}
public static final class ResourceFuture implements Future<URL> {
private InputStream input;
private final FutureTask<URL> delegate;
ResourceFuture(final File outputFile) {
delegate = new FutureTask<URL>(new Callable<URL>() {
public URL call() throws Exception {
if (input == null) {
throw new IllegalStateException();
}
URL outputURL;
try {
outputURL = outputFile.toURI().toURL();
} catch (MalformedURLException e) {
MarketplaceClientUi.error(e);
return null;
}
BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(outputFile));
boolean success = false;
try {
InputStream buffered = new BufferedInputStream(input);
int i;
while ((i = buffered.read()) != -1) {
output.write(i);
}
success = true;
} finally {
output.close();
if (!success || !outputFile.exists()) {
outputFile.delete();
outputURL = null;
}
}
return outputURL;
}
});
}
public boolean cancel(boolean mayInterruptIfRunning) {
return delegate.cancel(mayInterruptIfRunning);
}
public boolean isCancelled() {
return delegate.isCancelled();
}
public boolean isDone() {
return delegate.isDone();
}
public URL get() throws InterruptedException, ExecutionException {
return delegate.get();
}
public URL get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return delegate.get(timeout, unit);
}
FutureTask<URL> getDelegate() {
return delegate;
}
public URL retrieve(InputStream stream) throws IOException {
synchronized(this)
{
if (this.isDone() || this.input != null) {
return getURL();
}
this.input = stream;
}
try
{
delegate.run();
return getURL();
}
finally
{
synchronized (this) {
this.input = null;
}
}
}
public URL getURL() throws IOException {
try {
return get();
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
RuntimeException runtimeException = (RuntimeException) cause;
throw runtimeException;
} else if (cause instanceof IOException) {
IOException ioException = (IOException) cause;
throw ioException;
} else {
IOException ioException = new IOException(cause.getMessage());
ioException.initCause(cause);
throw ioException;
}
} catch (InterruptedException e) {
return null;
}
}
public URL getLocalURL() {
if (isDone() && !isCancelled()) {
try {
return getURL();
} catch (Exception ex) {
// this is explicitly ignored here...
}
}
return null;
}
}
private final File dir;
private final Map<String, ResourceFuture> resources = new ConcurrentHashMap<String, ResourceFuture>();
public ResourceProvider() throws IOException {
dir = File.createTempFile(ResourceProvider.class.getSimpleName(), ".tmp"); //$NON-NLS-1$
dir.delete();
if (!dir.mkdirs()) {
throw new IOException(dir.getAbsolutePath());
}
}
public URL getLocalResource(String resourceName) {
Future<URL> resource = getResource(resourceName);
try {
return resource == null ? null : resource.isCancelled() || !resource.isDone() ? null : resource.get();
} catch (Exception e) {
return null;
}
}
public ResourceFuture getResource(String resourceName) {
synchronized (resources) {
return resources.get(resourceName);
}
}
public boolean containsResource(String resourceName) {
synchronized (resources) {
return resources.containsKey(resourceName);
}
}
public ResourceFuture registerResource(String resourceName) throws IOException {
ResourceFuture resourceFuture;
synchronized (resources) {
resourceFuture = resources.get(resourceName);
if (resourceFuture == null) {
String filenameHint = resourceName;
if (filenameHint.lastIndexOf('/') != -1) {
filenameHint = filenameHint.substring(filenameHint.lastIndexOf('/') + 1);
}
filenameHint = filenameHint.replaceAll("[^a-zA-Z0-9\\.]", "_"); //$NON-NLS-1$ //$NON-NLS-2$
if (filenameHint.length() > 32) {
String hash = Integer.toHexString(filenameHint.hashCode());
filenameHint = filenameHint.substring(0, 6) + "_" //$NON-NLS-1$
+ hash + "_" //$NON-NLS-1$
+ filenameHint.substring(filenameHint.length() - (32 - hash.length() - 1 - 6 - 1));
}
final File outputFile = File.createTempFile("res_", filenameHint, dir); //$NON-NLS-1$
outputFile.deleteOnExit();
resourceFuture = new ResourceFuture(outputFile);
resources.put(resourceName, resourceFuture);
}
}
return resourceFuture;
}
public ResourceFuture retrieveResource(String requestSource, String resourceUrl) throws IOException,
URISyntaxException {
URI resourceUri = URLUtil.toURI(resourceUrl);
return retrieveResource(requestSource, resourceUrl, resourceUri);
}
public ResourceFuture retrieveResource(String requestSource, URI resourceUrl) throws IOException {
return retrieveResource(requestSource, resourceUrl.toString(), resourceUrl);
}
public ResourceFuture retrieveResource(final String requestSource, final String resourceName, final URI resourceUrl)
throws IOException {
ResourceFuture resourceFuture;
synchronized (resources) {
resourceFuture = resources.get(resourceName);
if (resourceFuture == null) {
final ResourceFuture finalResourceFuture = registerResource(resourceName);
resourceFuture = finalResourceFuture;
new Job(Messages.ResourceProvider_retrievingResource) {
{
setPriority(INTERACTIVE);
setUser(false);
setSystem(true);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
InputStream in = TransportFactory.createTransport().stream(resourceUrl, monitor);
finalResourceFuture.retrieve(in);
} catch (FileNotFoundException e) {
//MarketplaceClientUi.error(NLS.bind(Messages.AbstractResourceRunnable_resourceNotFound, new Object[] { catalogItem.getName(),
//catalogItem.getId(), resourceUrl }), e);
} catch (IOException e) {
if (e.getCause() instanceof OperationCanceledException) {
// canceled, nothing we want to do here
} else {
MarketplaceClientUi.log(IStatus.WARNING, Messages.ResourceProvider_downloadError,
requestSource, resourceUrl, e);
}
} catch (CoreException e) {
MarketplaceClientUi.log(IStatus.WARNING, Messages.ResourceProvider_downloadError,
requestSource, resourceUrl, e);
}
return Status.OK_STATUS;
}
}.schedule();
}
}
return resourceFuture;
}
public void dispose() {
if (dir != null && dir.exists()) {
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
file.delete();
}
}
dir.delete();
}
resources.clear();
}
public <T> void provideResource(final ResourceReceiver<T> receiver, final String resourcePath, T fallbackResource) {
final ResourceFuture resource = getResource(resourcePath);
if (resource != null) {
if (resource.isDone()) {
setResource(receiver, resourcePath, resource);
return;
}
if (fallbackResource != null) {
receiver.setResource(fallbackResource);
}
new Job(Messages.ResourceProvider_waitingForDownload) {
{
setPriority(INTERACTIVE);
setUser(false);
setSystem(true);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
return setResource(receiver, resourcePath, resource);
}
}.schedule();
} else if (fallbackResource != null) {
receiver.setResource(fallbackResource);
}
}
private <T> IStatus setResource(final ResourceReceiver<T> receiver, final String resourcePath,
final ResourceFuture resource) {
try {
URL resourceUrl = resource.get();
if (resourceUrl != null) {
T processedResource = receiver.processResource(resourceUrl);
receiver.setResource(processedResource);
}
} catch (InterruptedException e) {
return Status.CANCEL_STATUS;
} catch (ExecutionException e) {
// already logged during download
}
return Status.OK_STATUS;
}
}