/**
* Copyright (c) 2010, 2013 Darmstadt University of Technology.
* 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:
* Marcel Bruch - initial API and implementation.
*/
package org.eclipse.recommenders.internal.models.rcp;
import static com.google.common.base.Optional.absent;
import static org.eclipse.recommenders.internal.models.rcp.ModelsRcpModule.REPOSITORY_BASEDIR;
import static org.eclipse.recommenders.models.ModelCoordinate.HINT_REPOSITORY_URL;
import static org.eclipse.recommenders.utils.Urls.mangle;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.LinkedList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.commons.io.FileUtils;
import org.eclipse.core.net.proxy.IProxyData;
import org.eclipse.core.net.proxy.IProxyService;
import org.eclipse.recommenders.internal.models.rcp.l10n.LogMessages;
import org.eclipse.recommenders.models.DownloadCallback;
import org.eclipse.recommenders.models.IModelRepository;
import org.eclipse.recommenders.models.ModelCoordinate;
import org.eclipse.recommenders.models.ModelRepository;
import org.eclipse.recommenders.models.rcp.ModelEvents.ModelRepositoryClosedEvent;
import org.eclipse.recommenders.models.rcp.ModelEvents.ModelRepositoryOpenedEvent;
import org.eclipse.recommenders.models.rcp.ModelEvents.ModelRepositoryUrlChangedEvent;
import org.eclipse.recommenders.rcp.IRcpService;
import org.eclipse.recommenders.utils.Checks;
import org.eclipse.recommenders.utils.Logs;
import org.eclipse.recommenders.utils.Pair;
import org.eclipse.recommenders.utils.Uris;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
/**
* The Eclipse RCP wrapper around an {@link IModelRepository} that responds to (@link ModelRepositoryChangedEvent)s by
* reconfiguring the underlying repository. It also manages proxy settings and handling of auto download properties.
*/
public class EclipseModelRepository implements IModelRepository, IRcpService {
private final File basedir;
private final IProxyService proxy;
private final ModelsRcpPreferences prefs;
private final EventBus bus;
private final List<Pair<String, ModelRepository>> delegates = Lists.newLinkedList();
private boolean isOpen = false;
@Inject
public EclipseModelRepository(@Named(REPOSITORY_BASEDIR) File basedir, IProxyService proxy,
ModelsRcpPreferences prefs, EventBus bus) {
this.basedir = basedir;
this.proxy = proxy;
this.prefs = prefs;
this.bus = bus;
}
@PostConstruct
void open() throws Exception {
String[] remoteUrls = prefs.remotes;
List<Pair<String, ModelRepository>> repositories = Lists.newLinkedList();
for (String remoteUrl : remoteUrls) {
File cache = new File(basedir, mangle(remoteUrl));
cache.mkdirs();
repositories.add(Pair.newPair(remoteUrl, new ModelRepository(cache, remoteUrl)));
}
openInternal(repositories);
}
@VisibleForTesting
void openInternal(List<Pair<String, ModelRepository>> repositories) {
delegates.clear();
delegates.addAll(repositories);
isOpen = true;
bus.post(new ModelRepositoryOpenedEvent());
}
@VisibleForTesting
protected void close() {
closeSilently();
bus.post(new ModelRepositoryClosedEvent());
}
@PreDestroy
void closeSilently() {
isOpen = false;
}
@Subscribe
public void onModelRepositoryChanged(ModelRepositoryUrlChangedEvent e) throws Exception {
close();
open();
}
private List<ModelRepository> searchDelegates(ModelCoordinate mc) {
LinkedList<ModelRepository> result = Lists.newLinkedList();
String repoUrl = mc.getHint(HINT_REPOSITORY_URL).orNull();
for (Pair<String, ModelRepository> delegate : delegates) {
if (repoUrl == null || repoUrl.equals(delegate.getFirst())) {
result.add(delegate.getSecond());
}
}
return result;
}
@Override
public Optional<File> getLocation(final ModelCoordinate mc, boolean prefetch) {
ensureIsOpen();
List<ModelRepository> foundDelegates = searchDelegates(mc);
for (ModelRepository delegate : foundDelegates) {
Optional<File> location = delegate.getLocation(mc, false);
if (prefetch && prefs.autoDownloadEnabled) {
new DownloadModelArchiveJob(this, mc, false, bus).schedule();
}
if (location.isPresent()) {
return location;
}
}
return absent();
}
@Override
public Optional<File> resolve(ModelCoordinate mc, boolean force) {
ensureIsOpen();
updateAuthenticationSettings();
updateProxySettings();
List<ModelRepository> foundSuitableDelegates = searchDelegates(mc);
for (ModelRepository modelRepository : foundSuitableDelegates) {
Optional<File> location = modelRepository.resolve(mc, force);
if (location.isPresent()) {
return location;
}
}
return absent();
}
@Override
public Optional<File> resolve(ModelCoordinate mc, boolean force, DownloadCallback callback) {
ensureIsOpen();
updateAuthenticationSettings();
updateProxySettings();
List<ModelRepository> foundSuitableDelegates = searchDelegates(mc);
for (ModelRepository modelRepository : foundSuitableDelegates) {
Optional<File> location = modelRepository.resolve(mc, force, callback);
if (location.isPresent()) {
return location;
}
}
return absent();
}
private void updateAuthenticationSettings() {
for (Pair<String, ModelRepository> delegate : delegates) {
updateAuthenticationSettings(delegate.getFirst(), delegate.getSecond());
}
}
private void updateAuthenticationSettings(String repositoryUri, ModelRepository modelRepository) {
URI serverUri = Uris.toUri(repositoryUri);
if (Uris.hasCredentials(serverUri)) {
// Credentials encoded in the URL take precedence
}
String username = prefs.getServerUsername(repositoryUri).orNull();
if (!Strings.isNullOrEmpty(username)) {
String password = prefs.getServerPassword(repositoryUri).orNull();
modelRepository.setAuthentication(username, password);
}
}
private void updateProxySettings() {
for (Pair<String, ModelRepository> delegate : delegates) {
updateProxySettings(delegate.getFirst(), delegate.getSecond());
}
}
private void updateProxySettings(String repositoryUri, ModelRepository modelRepository) {
if (!proxy.isProxiesEnabled()) {
modelRepository.unsetProxy();
return;
}
try {
URI uri = new URI(repositoryUri);
if (uri.getScheme() == null) {
return;
}
if (uri.getHost() == null) {
return;
}
IProxyData[] entries = proxy.select(uri);
if (entries.length == 0) {
modelRepository.unsetProxy();
return;
}
IProxyData proxyData = entries[0];
String type = proxyData.getType().toLowerCase();
String host = proxyData.getHost();
int port = proxyData.getPort();
String userId = proxyData.getUserId();
String password = proxyData.getPassword();
modelRepository.setProxy(type, host, port, userId, password);
} catch (URISyntaxException e) {
modelRepository.unsetProxy();
}
}
public void deleteModels() throws IOException {
try {
close();
FileUtils.cleanDirectory(basedir);
} finally {
try {
open();
} catch (Exception e) {
Logs.log(LogMessages.ERROR_FAILED_TO_OPEN_ECLIPSE_MODEL_REPOSITORY_FOLLOWING_MODEL_DELETION, e);
}
}
}
private void ensureIsOpen() {
Checks.ensureIsTrue(isOpen, "model repository service is not accesible at the moment."); //$NON-NLS-1$
}
@Override
public String toString() {
return Objects.toStringHelper(this).addValue(delegates).toString();
}
}