blob: cd87ba03b6388bc3205ddb52880c7ac8ac969340 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2012 Tasktop Technologies 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:
* Tasktop Technologies - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.commons.repositories.core;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.SocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.core.runtime.Status;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.commons.core.net.NetUtil;
import org.eclipse.mylyn.commons.repositories.core.RepositoryLocationChangeEvent.Type;
import org.eclipse.mylyn.commons.repositories.core.auth.AuthenticationCredentials;
import org.eclipse.mylyn.commons.repositories.core.auth.AuthenticationRequest;
import org.eclipse.mylyn.commons.repositories.core.auth.AuthenticationType;
import org.eclipse.mylyn.commons.repositories.core.auth.ICredentialsStore;
import org.eclipse.mylyn.commons.repositories.core.auth.UserCredentials;
import org.eclipse.mylyn.internal.commons.repositories.core.CredentialsFactory;
import org.eclipse.mylyn.internal.commons.repositories.core.InMemoryCredentialsStore;
import org.eclipse.mylyn.internal.commons.repositories.core.LocationService;
import org.eclipse.mylyn.internal.commons.repositories.core.RepositoriesCoreInternal;
/**
* @author Steffen Pingel
*/
// FIXME add synchronization
public class RepositoryLocation extends PlatformObject {
private static boolean flushCredentialsErrorLogged;
public static final String PROPERTY_ID = "id"; //$NON-NLS-1$
public static final String PROPERTY_LABEL = "label"; //$NON-NLS-1$
public static final String PROPERTY_OFFLINE = "org.eclipse.mylyn.tasklist.repositories.offline"; //$NON-NLS-1$
public static final String PROPERTY_PROXY_HOST = "org.eclipse.mylyn.repositories.proxy.host"; //$NON-NLS-1$
public static final String PROPERTY_PROXY_PORT = "org.eclipse.mylyn.repositories.proxy.port"; //$NON-NLS-1$
public static final String PROPERTY_PROXY_USEDEFAULT = "org.eclipse.mylyn.repositories.proxy.usedefault"; //$NON-NLS-1$
public static final String PROPERTY_URL = "url"; //$NON-NLS-1$
public static final String PROPERTY_USERNAME = "org.eclipse.mylyn.repositories.username"; //$NON-NLS-1$
private static final String ENABLED = ".enabled"; //$NON-NLS-1$
private static Map<String, String> createDefaultProperties() {
Map<String, String> defaultProperties = new HashMap<String, String>();
defaultProperties.put(PROPERTY_PROXY_USEDEFAULT, Boolean.TRUE.toString());
return defaultProperties;
}
private ICredentialsStore credentialsStore;
// transient
private IStatus errorStatus = null;
private final Map<String, String> properties = new LinkedHashMap<String, String>();
private final List<PropertyChangeListener> propertyChangeListeners = new CopyOnWriteArrayList<PropertyChangeListener>();
private final List<IRepositoryLocationChangeListener> repositoryLocationChangeListeners = new CopyOnWriteArrayList<IRepositoryLocationChangeListener>();
private ILocationService service;
private final boolean workingCopy;
public RepositoryLocation() {
this(createDefaultProperties(), LocationService.getDefault(), false);
}
public RepositoryLocation(String url) {
this();
setUrl(url);
}
public RepositoryLocation(Map<String, String> properties) {
this(properties, LocationService.getDefault(), true);
}
public RepositoryLocation(Map<String, String> properties, ILocationService service, boolean workingCopy) {
this.properties.putAll(properties);
this.service = service;
this.workingCopy = workingCopy;
if (this.properties.get(PROPERTY_ID) == null) {
this.properties.put(RepositoryLocation.PROPERTY_ID, UUID.randomUUID().toString());
}
}
public RepositoryLocation(RepositoryLocation source) {
this(source.getProperties(), source.getService(), true);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeListeners.add(listener);
}
public void apply(RepositoryLocation location) {
String oldId = getProperty(PROPERTY_ID);
ICredentialsStore oldCredentialsStore = null;
if (oldId != null) {
oldCredentialsStore = getCredentialsStore();
}
// merge properties
HashSet<String> removed = new HashSet<String>(properties.keySet());
removed.removeAll(location.properties.keySet());
for (Map.Entry<String, String> entry : location.properties.entrySet()) {
setProperty(entry.getKey(), entry.getValue());
}
for (String key : removed) {
setProperty(key, null);
}
String newId = getProperty(PROPERTY_ID);
if (newId != null) {
// migrate credentials if url has changed
ICredentialsStore newCredentialsStore = getCredentialsStore();
if (!newId.equals(oldId)) {
if (oldCredentialsStore != null) {
oldCredentialsStore.copyTo(newCredentialsStore);
oldCredentialsStore.clear();
}
}
// merge credentials
if (location.getCredentialsStore() instanceof InMemoryCredentialsStore) {
((InMemoryCredentialsStore) location.getCredentialsStore()).copyTo(newCredentialsStore);
}
// persist changes
try {
newCredentialsStore.flush();
} catch (IOException e) {
if (!flushCredentialsErrorLogged) {
flushCredentialsErrorLogged = true;
StatusHandler.log(new Status(
IStatus.ERROR,
RepositoriesCoreInternal.ID_PLUGIN,
"Unexpected error occured while flushing credentials. Credentials may not have been saved.", e)); //$NON-NLS-1$
}
}
}
fireRepositoryLocationChangeEvent(Type.ALL);
}
public void clearCredentials() {
getCredentialsStore().clear();
}
public boolean getBooleanPropery(String key) {
String value = getProperty(key);
return value != null && Boolean.parseBoolean(value);
}
public <T extends AuthenticationCredentials> T getCredentials(AuthenticationType<T> authType) {
return getCredentials(authType, true);
}
public <T extends AuthenticationCredentials> T getCredentials(AuthenticationType<T> authType, boolean loadSecrets) {
String prefix = authType.getKey();
if (getBooleanPropery(prefix + ENABLED)) {
if (getId() == null) {
// can't determine location of credentials
return null;
}
return CredentialsFactory.create(authType.getCredentialsType(), getCredentialsStore(), prefix, loadSecrets);
}
return null;
}
public ICredentialsStore getCredentialsStore() {
if (credentialsStore == null) {
return getService().getCredentialsStore(getId());
}
return credentialsStore;
}
public String getId() {
String id = getProperty(PROPERTY_ID);
if (id == null) {
throw new IllegalStateException("Repository ID is not set"); //$NON-NLS-1$
}
return id;
}
/**
* @return the URL if the label property is not set
*/
public String getLabel() {
String label = properties.get(PROPERTY_LABEL);
if (label != null && label.length() > 0) {
return label;
} else {
return getUrl();
}
}
public Map<String, String> getProperties() {
return new LinkedHashMap<String, String>(this.properties);
}
public String getProperty(String name) {
return this.properties.get(name);
}
public Proxy getProxy() {
if (Boolean.parseBoolean(getProperty(PROPERTY_PROXY_USEDEFAULT))) {
return null;
}
String proxyHost = getProperty(PROPERTY_PROXY_HOST);
String proxyPort = getProperty(PROPERTY_PROXY_PORT);
if (proxyHost != null && proxyHost.length() > 0 && proxyPort != null) {
try {
int proxyPortNum = Integer.parseInt(proxyPort);
UserCredentials credentials = getCredentials(AuthenticationType.PROXY);
if (credentials != null) {
return NetUtil.createProxy(proxyHost, proxyPortNum, credentials.getUserName(),
credentials.getPassword(), credentials.getDomain());
} else {
return NetUtil.createProxy(proxyHost, proxyPortNum);
}
} catch (NumberFormatException e) {
StatusHandler.log(new Status(IStatus.ERROR, RepositoriesCoreInternal.ID_PLUGIN, 0,
"Error occured while configuring proxy. Invalid port \"" //$NON-NLS-1$
+ proxyPort + "\" specified.", e)); //$NON-NLS-1$
}
}
return null;
}
// FIXME e3.5 replace with 3.5 proxy API
public Proxy getProxyForHost(String host, String proxyType) {
Proxy proxy = getProxy();
if (proxy != null) {
return proxy;
}
return getService().getProxyForHost(host, proxyType);
}
public ILocationService getService() {
return service;
}
public IStatus getStatus() {
return errorStatus;
}
public String getUrl() {
return getProperty(PROPERTY_URL);
}
/**
* The username is cached since it needs to be retrieved frequently (e.g. for Task List decoration).
*/
public String getUserName() {
return getProperty(PROPERTY_USERNAME);
}
public boolean hasProperty(String name) {
String value = getProperty(name);
return value != null && value.trim().length() > 0;
}
/**
* Returns true if a normalized form of <code>url</code> matches the URL of this location.
*/
public boolean hasUrl(String url) {
Assert.isNotNull(url);
String myUrl = getUrl();
if (myUrl == null) {
return false;
}
try {
return new URI(url + "/").normalize().equals(new URI(myUrl + "/").normalize()); //$NON-NLS-1$//$NON-NLS-2$
} catch (URISyntaxException e) {
return false;
}
}
public boolean isOffline() {
return Boolean.parseBoolean(getProperty(PROPERTY_OFFLINE));
}
public boolean isWorkingCopy() {
return workingCopy;
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeListeners.remove(listener);
}
public <T extends AuthenticationCredentials> void removeCredentials(AuthenticationType<T> authType, T credentials) {
String prefix = authType.getKey();
credentials.clear(getCredentialsStore(), prefix);
}
public void removeProperty(String key) {
setProperty(key, null);
}
/**
* Requests credentials. This may block and prompt to enter credentials.
*
* @param request
* the authentication request
* @param monitor
* the progress monitor
* @return the entered credentials
* @see ILocationService#requestCredentials(AuthenticationRequest, IProgressMonitor)
*/
public <T extends AuthenticationCredentials> T requestCredentials(
AuthenticationRequest<AuthenticationType<T>> request, IProgressMonitor monitor) {
return getService().requestCredentials(request, monitor);
}
public <T extends AuthenticationCredentials> void setCredentials(AuthenticationType<T> authType, T credentials) {
String prefix = authType.getKey();
if (credentials == null) {
setProperty(prefix + ENABLED, String.valueOf(false));
} else {
setProperty(prefix + ENABLED, String.valueOf(true));
credentials.save(getCredentialsStore(), prefix);
}
fireRepositoryLocationChangeEvent(Type.CREDENTIALS);
}
public void setCredentialsStore(ICredentialsStore credentialsStore) {
this.credentialsStore = credentialsStore;
}
public void setIdPreservingCredentialsStore(String id) {
Assert.isNotNull(id);
ICredentialsStore store = getCredentialsStore();
setProperty(RepositoryLocation.PROPERTY_ID, id);
if (this.credentialsStore == null) {
setCredentialsStore(store);
}
}
public void setLabel(String label) {
setProperty(PROPERTY_LABEL, label);
}
public void setOffline(boolean offline) {
properties.put(PROPERTY_OFFLINE, String.valueOf(offline));
}
public void setProperty(String key, String newValue) {
validatePropertyChange(key, newValue);
String oldValue = this.properties.get(key);
if (hasChanged(oldValue, newValue)) {
this.properties.put(key.intern(), (newValue != null) ? newValue.intern() : null);
handlePropertyChange(key, oldValue, newValue);
}
}
public void validatePropertyChange(String key, String newValue) {
Assert.isNotNull(key);
if (key.equals(RepositoryLocation.PROPERTY_ID) && newValue == null) {
throw new IllegalArgumentException("The ID property must not be null"); //$NON-NLS-1$
}
}
public void setProxy(Proxy proxy) {
if (proxy == null) {
setProperty(PROPERTY_PROXY_USEDEFAULT, Boolean.toString(true));
} else {
SocketAddress address = proxy.address();
if (address instanceof InetSocketAddress) {
setProperty(PROPERTY_PROXY_HOST, ((InetSocketAddress) address).getHostName());
setProperty(PROPERTY_PROXY_PORT, Integer.toString(((InetSocketAddress) address).getPort()));
setProperty(PROPERTY_PROXY_USEDEFAULT, Boolean.toString(false));
} else {
throw new IllegalArgumentException("Invalid proxy address"); //$NON-NLS-1$
}
}
fireRepositoryLocationChangeEvent(Type.PROYX);
}
public void setService(ILocationService service) {
this.service = service;
}
public void setStatus(IStatus errorStatus) {
this.errorStatus = errorStatus;
}
public void setUrl(String url) {
setProperty(PROPERTY_URL, url);
}
public void setUserName(String userName) {
setProperty(PROPERTY_USERNAME, userName);
}
@Override
public String toString() {
return getLabel();
}
private void handlePropertyChange(String key, Object old, Object value) {
if (PROPERTY_ID.equals(key)) {
credentialsStore = null;
}
firePropertyChangeEvent(key, old, value);
}
private void firePropertyChangeEvent(String key, Object old, Object value) {
PropertyChangeEvent event = new PropertyChangeEvent(this, key, old, value);
for (PropertyChangeListener listener : propertyChangeListeners) {
listener.propertyChange(event);
}
}
private void fireRepositoryLocationChangeEvent(RepositoryLocationChangeEvent.Type type) {
RepositoryLocationChangeEvent event = new RepositoryLocationChangeEvent(this, type);
for (IRepositoryLocationChangeListener listener : repositoryLocationChangeListeners) {
listener.repositoryChanged(event);
}
}
private boolean hasChanged(Object oldValue, Object newValue) {
return oldValue != null && !oldValue.equals(newValue) || oldValue == null && newValue != null;
}
}