gt:       File cacheFile = getCacheFile(uri);
context: /*
   * Copyright (c) 2014, 2015 Eike Stepper (Berlin, Germany) 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:
   *    Eike Stepper - initial API and implementation
   */
  package org.eclipse.oomph.p2.internal.core;
  
  import org.eclipse.oomph.util.IOExceptionWithCause;
  import org.eclipse.oomph.util.IORuntimeException;
  import org.eclipse.oomph.util.IOUtil;
  import org.eclipse.oomph.util.OfflineMode;
  import org.eclipse.oomph.util.PropertiesUtil;
  import org.eclipse.oomph.util.ReflectUtil;
  
  import org.eclipse.core.runtime.CoreException;
  import org.eclipse.core.runtime.IProgressMonitor;
  import org.eclipse.core.runtime.IStatus;
  import org.eclipse.core.runtime.Status;
  import org.eclipse.equinox.internal.p2.repository.AuthenticationFailedException;
  import org.eclipse.equinox.internal.p2.repository.CacheManager;
  import org.eclipse.equinox.internal.p2.repository.DownloadStatus;
  import org.eclipse.equinox.internal.p2.repository.Transport;
  import org.eclipse.equinox.internal.provisional.p2.repository.IStateful;
  import org.eclipse.equinox.p2.core.IProvisioningAgent;
  
  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.lang.reflect.Method;
  import java.net.SocketTimeoutException;
  import java.net.URI;
  import java.net.URISyntaxException;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.Stack;
  
  /**
   * @author Eike Stepper
   */
  @SuppressWarnings("restriction")
  public class CachingTransport extends Transport
  {
    public static final String SERVICE_NAME = Transport.SERVICE_NAME;
  
    private static final ThreadLocal<Stack<String>> REPOSITORY_LOCATIONS = new InheritableThreadLocal<Stack<String>>()
    {
      @Override
      protected Stack<String> initialValue()
      {
        return new Stack<String>();
      }
    };
  
    private static final Map<URI, Object> URI_LOCKS = new HashMap<URI, Object>();
  
    private static boolean DEBUG = false;
  
    private final IProvisioningAgent agent;
  
    private final File cacheFolder;
  
    private Transport delegate;
  
    public CachingTransport(Transport delegate, IProvisioningAgent agent)
    {
      setDelegate(delegate);
      this.agent = agent;
  
      File folder = P2CorePlugin.getUserStateFolder(new File(PropertiesUtil.getOomphHome()));
      cacheFolder = new File(folder, "cache");
      cacheFolder.mkdirs();
    }
  
    public final Transport getDelegate()
    {
      return delegate;
    }
  
    public final void setDelegate(Transport delegate)
    {
      if (delegate instanceof CachingTransport)
      {
        throw new IllegalArgumentException("CachingTransport should not be chained");
      }
  
      this.delegate = delegate;
    }
  
    public File getCacheFile(URI uri)
    {
      return new File(cacheFolder, IOUtil.encodeFileName(uri.toString()));
    }
  
    @Override
    public IStatus download(URI uri, OutputStream target, long startPos, IProgressMonitor monitor)
    {
      if (DEBUG)
      {
        log("  ! " + uri);
      }
  
      if (!isLoadingRepository(uri))
      {
        return delegate.download(uri, target, startPos, monitor);
      }
  
      synchronized (getLock(uri))
      {
        File cacheFile = getCacheFile(uri);
        if (cacheFile.length() > 0)
        {
          FileInputStream cacheInputStream = null;
  
          try
          {
            cacheInputStream = new FileInputStream(cacheFile);
            IOUtil.copy(cacheInputStream, target);
            removeLock(uri);
            return Status.OK_STATUS;
          }
          catch (Exception ex)
          {
            //$FALL-THROUGH$
          }
          finally
          {
            IOUtil.closeSilent(cacheInputStream);
          }
        }
  
        // If the offline mode is not enabled we must make p2 and (for later) ourselves happy.
        StatefulFileOutputStream statefulTarget = null;
        FileInputStream cacheInputStream = null;
  
        try
        {
          cacheFile.getParentFile().mkdirs();
          statefulTarget = new StatefulFileOutputStream(cacheFile);
  
          IStatus status = delegate.download(uri, statefulTarget, startPos, monitor);
          IOUtil.closeSilent(statefulTarget);
          if (status.isOK())
          {
            // Files can be many megabytes large, so download them directly to a file.
            cacheInputStream = new FileInputStream(cacheFile);
            IOUtil.copy(cacheInputStream, target);
  
            DownloadStatus downloadStatus = (DownloadStatus)status;
            long lastModified = downloadStatus.getLastModified();
            if (lastModified >= 0)
            {
              cacheFile.setLastModified(lastModified);
            }
  
            // Remove the other form that might be cached.
            String path = cacheFile.getPath();
            if (path.endsWith(".xml"))
            {
              new File(path.substring(0, path.length() - 4) + ".jar").delete();
            }
            else if (path.endsWith(".jar"))
            {
              new File(path.substring(0, path.length() - 4) + ".xml").delete();
            }
          }
          else
          {
            IOUtil.deleteBestEffort(cacheFile);
          }
  
          return status;
        }
        catch (IOException ex)
        {
          throw new IORuntimeException(ex);
        }
        finally
        {
          if (target instanceof IStateful && statefulTarget != null)
          {
            ((IStateful)target).setStatus(statefulTarget.getStatus());
          }
  
          IOUtil.closeSilent(cacheInputStream);
          IOUtil.closeSilent(statefulTarget);
  
          removeLock(uri);
        }
      }
    }
  
    @Override
    public IStatus download(URI uri, OutputStream target, IProgressMonitor monitor)
    {
      return download(uri, target, 0, monitor);
    }
  
    @Override
    public InputStream stream(URI uri, IProgressMonitor monitor) throws FileNotFoundException, CoreException, AuthenticationFailedException
    {
      return delegate.stream(uri, monitor);
    }
  
    @Override
    public long getLastModified(URI uri, IProgressMonitor monitor) throws CoreException, FileNotFoundException, AuthenticationFailedException
    {
      if (DEBUG)
      {
        log("  ? " + uri);
      }
  
      if (isLoadingRepository(uri) && OfflineMode.isEnabled())
      {
  