blob: 87a4d7f32b07fd020c858e2ef5c1c26ee61c424a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2018 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 v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* The Eclipse Foundation - initial API and implementation
*******************************************************************************/
package org.eclipse.epp.internal.mpc.core.util;
import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
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.epp.internal.mpc.core.MarketplaceClientCore;
import org.eclipse.epp.mpc.core.service.ITransport;
import org.eclipse.epp.mpc.core.service.ITransportFactory;
import org.eclipse.epp.mpc.core.service.ServiceUnavailableException;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
public class FallbackTransportFactory implements ITransportFactory {
private static final class FallbackTransport implements ITransport {
private final ITransport primaryTransport;
private ITransport fallbackTransport;
private boolean primaryDisabled = false;
FallbackTransport(ITransport primaryTransport, ITransport fallbackTransport) {
super();
this.primaryTransport = primaryTransport;
this.fallbackTransport = fallbackTransport;
}
private final Set<String> reportedProblems = new HashSet<>();
private int connectionAttempts;
private int connectionFailures;
@Override
public InputStream stream(URI location, IProgressMonitor monitor)
throws FileNotFoundException, ServiceUnavailableException, CoreException {
connectionAttempts++;
if (connectionAttempts > 10 && connectionFailures / (double) connectionAttempts > 0.75) {
MarketplaceClientCore.getLog()
.log(new Status(IStatus.INFO, MarketplaceClientCore.BUNDLE_ID,
NLS.bind(Messages.FallbackTransportFactory_disablingTransport, primaryTransport)));
primaryDisabled = true;
}
if (primaryTransport == null || primaryDisabled) {
return fallbackTransport.stream(location, monitor);
}
InputStream stream;
try {
stream = primaryTransport.stream(location, monitor);
if (stream == null) {
throw new NullPointerException();
}
} catch (FileNotFoundException ex) {
InputStream fallbackStream = primaryFailed(location, monitor, ex);
if (fallbackStream == null) {
throw ex;
}
return fallbackStream;
} catch (ServiceUnavailableException ex) {
InputStream fallbackStream = primaryFailed(location, monitor, ex);
if (fallbackStream == null) {
throw ex;
}
return fallbackStream;
} catch (CoreException ex) {
InputStream fallbackStream = primaryFailed(location, monitor, ex);
if (fallbackStream == null) {
throw ex;
}
return fallbackStream;
} catch (RuntimeException ex) {
InputStream fallbackStream = primaryFailed(location, monitor, ex);
if (fallbackStream == null) {
throw ex;
}
return fallbackStream;
}
try {
BufferedInputStream buffered = new BufferedInputStream(stream);
tryBuffer(buffered);
return buffered;
} catch (IOException ex) {
InputStream fallbackStream = primaryFailed(location, monitor, ex);
if (fallbackStream == null) {
throw new CoreException(MarketplaceClientCore.computeStatus(ex, null));
}
return fallbackStream;
}
}
private static void tryBuffer(BufferedInputStream buffered) throws IOException {
buffered.mark(128);
try {
buffered.read(new byte[128]);
} finally {
buffered.reset();
}
}
private InputStream primaryFailed(URI location, IProgressMonitor monitor, Exception ex)
throws FileNotFoundException, ServiceUnavailableException, CoreException {
connectionFailures++;
if (fallbackTransport != null) {
boolean fallbackSucceeded = false;
try {
InputStream fallbackStream = fallbackTransport.stream(location, monitor);
BufferedInputStream buffered = new BufferedInputStream(fallbackStream);
tryBuffer(buffered);
fallbackSucceeded = true;
String problemKey = ex.getClass().getName() + ": " + ex.getMessage() + "\n\t" //$NON-NLS-1$//$NON-NLS-2$
+ ex.getStackTrace()[0];
if (reportedProblems.add(problemKey)) {
MarketplaceClientCore.getLog()
.log(MarketplaceClientCore.computeStatus(ex,
NLS.bind(Messages.FallbackTransportFactory_fallbackStream, primaryTransport,
fallbackTransport)));
}
return buffered;
} catch (Exception fallbackEx) {
ex.addSuppressed(fallbackEx);
} finally {
if (!fallbackSucceeded) {
//fallback didn't work either - probably something unrelated to transport going on, so don't count this as a transport failure
connectionFailures--;
}
}
}
return null;
}
void setFallbackTransport(ITransport fallbackTransport) {
this.fallbackTransport = fallbackTransport;
}
public ITransport getPrimaryTransport() {
return primaryTransport;
}
public ITransport getFallbackTransport() {
return fallbackTransport;
}
}
private ITransportFactory primaryFactory;
private ITransportFactory secondaryFactory;
private FallbackTransport transport;
public FallbackTransportFactory() {
super();
// ignore
}
@Override
public synchronized ITransport getTransport() {
ITransportFactory delegateFactory = getFallbackFactory();
ITransport primaryTransport = primaryFactory.getTransport();
if (delegateFactory == null) {
return primaryTransport;
}
ITransport secondaryTransport = delegateFactory.getTransport();
if (transport == null || transport.getPrimaryTransport() != primaryTransport
|| transport.getFallbackTransport() != secondaryTransport) {
transport = new FallbackTransport(primaryTransport, secondaryTransport);
}
return transport;
}
public ITransportFactory getFallbackFactory() {
ITransportFactory delegateFactory = this.secondaryFactory;
if (delegateFactory == null) {
BundleContext bundleContext = FrameworkUtil.getBundle(getClass()).getBundleContext();
try {
String disabledTransportsFilter = TransportFactory.computeDisabledTransportsFilter();
Collection<ServiceReference<ITransportFactory>> serviceReferences = bundleContext.getServiceReferences(
ITransportFactory.class, "".equals(disabledTransportsFilter) ? null : disabledTransportsFilter); //$NON-NLS-1$
if (!serviceReferences.isEmpty()) {
for (ServiceReference<ITransportFactory> serviceReference : serviceReferences) {
ITransportFactory service = bundleContext.getService(serviceReference);
if (service != this && service != primaryFactory
&& !"org.eclipse.epp.mpc.tests.service.MappedTransportFactory" //$NON-NLS-1$
.equals(service.getClass().getName())) {
delegateFactory = service;
break;
} else {
bundleContext.ungetService(serviceReference);
}
}
}
} catch (InvalidSyntaxException e) {
//impossible
}
}
return delegateFactory;
}
public ITransportFactory getPrimaryFactory() {
return primaryFactory;
}
public void setPrimaryFactory(ITransportFactory primaryFactory) {
this.primaryFactory = primaryFactory;
}
public void bindPrimaryFactory(ITransportFactory factory) {
setPrimaryFactory(factory);
}
public void unbindPrimaryFactory(ITransportFactory factory) {
if (primaryFactory == factory) {
setPrimaryFactory(null);
}
}
public ITransportFactory getSecondaryFactory() {
return secondaryFactory;
}
public void setSecondaryFactory(ITransportFactory secondaryFactory) {
this.secondaryFactory = secondaryFactory;
}
}