blob: 2a19721fc8d82faad92378ea6dcbe277e1d1464e [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
*******************************************************************************/
package org.eclipse.epp.internal.mpc.core.service;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.epp.internal.mpc.core.MarketplaceClientCore;
import org.eclipse.epp.internal.mpc.core.util.ServiceUtil;
import org.eclipse.epp.mpc.core.service.IMarketplaceStorageService;
import org.eclipse.equinox.security.storage.ISecurePreferences;
import org.eclipse.equinox.security.storage.StorageException;
import org.eclipse.osgi.util.NLS;
import org.eclipse.userstorage.IBlob;
import org.eclipse.userstorage.IStorage;
import org.eclipse.userstorage.IStorageService;
import org.eclipse.userstorage.StorageFactory;
import org.eclipse.userstorage.internal.StorageService;
import org.eclipse.userstorage.oauth.EclipseOAuthCredentialsProvider;
import org.eclipse.userstorage.spi.Credentials;
import org.eclipse.userstorage.spi.ICredentialsProvider;
import org.eclipse.userstorage.spi.ISettings;
import org.eclipse.userstorage.util.FileStorageCache;
import org.eclipse.userstorage.util.Settings;
import org.osgi.framework.BundleContext;
@SuppressWarnings("restriction")
public class MarketplaceStorageService implements IMarketplaceStorageService {
private static final String DEFAULT_STORAGE_SERVICE_NAME = Messages.MarketplaceStorageService_defaultStorageServiceName;
static final String DEFAULT_APPLICATION_TOKEN = "MZ04RMOpksKN5GpxKXafq2MSjSP"; //$NON-NLS-1$
private static final String[] MIGRATE_SECURE_STORAGE_KEYS = { "username", "password", "termsOfUseAgreed" }; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
private String applicationToken = DEFAULT_APPLICATION_TOKEN;
private URI serviceUri;
private StorageFactory storageFactory;
private IStorage storage;
private IStorageService.Dynamic customStorageService;
private List<LoginListener> loginListeners;
private EclipseOAuthCredentialsProvider credentialsProvider;
private StorageConfigurer configurer;
public URI getServiceUri() {
return serviceUri;
}
public synchronized void setServiceUri(URI serviceUri) {
if ((this.serviceUri == null && serviceUri != null)
|| (this.serviceUri != null && !this.serviceUri.equals(serviceUri))) {
this.serviceUri = serviceUri;
storageFactory = null;
storage = null;
}
}
public synchronized StorageFactory getStorageFactory() {
if (storageFactory == null) {
storageFactory = createStorageFactory();
}
return storageFactory;
}
public synchronized void setStorageFactory(StorageFactory storageFactory) {
this.storageFactory = storageFactory;
}
protected StorageFactory createStorageFactory() {
if (serviceUri == null) {
return StorageFactory.DEFAULT;
}
Map<String, String> settingsMap = new HashMap<String, String>();
settingsMap.put(applicationToken, serviceUri.toString());
ISettings storageFactorySettings = new Settings.MemorySettings(settingsMap);
return new StorageFactory(storageFactorySettings);
}
protected IStorage createStorage() {
IStorage storage = getStorageFactory().create(applicationToken,
new FileStorageCache.SingleApplication(this.applicationToken));
try {
configurer = StorageConfigurer.get();
configurer.configure(storage);
configurer.setInteractive(storage, false);
} catch (CoreException e) {
configurer = null;
MarketplaceClientCore.error(e);
}
return storage;
}
public synchronized IStorage getStorage() {
if (storage == null) {
storage = createStorage();
}
return storage;
}
public void setStorage(IStorage storage) {
this.storage = storage;
}
public IBlob getBlob(String key) {
return getStorage().getBlob(key);
}
public String getRegisteredUser() {
Credentials credentials = getStorageCredentials();
return credentials == null ? null : credentials.getUsername();
}
public <T> T runWithLogin(Callable<T> c) throws Exception {
String oldUser = getCurrentUser();
try {
T result = configurer != null ? configurer.runWithLogin(storage, c) : c.call();
return result;
} finally {
String newUser = getCurrentUser();//TODO the OAuth token might change with the user staying the same - this would cause an unnecessary refresh event
notifyLoginChanged(oldUser, newUser);
}
}
private void notifyLoginChanged(String oldUser, String newUser) {
if ((newUser == null && oldUser != null) || (newUser != null && !newUser.equals(oldUser))) {
List<LoginListener> loginListeners = this.loginListeners;
if (loginListeners != null && !loginListeners.isEmpty()) {
for (LoginListener loginListener : loginListeners) {
loginListener.loginChanged(oldUser, newUser);
}
}
}
}
public synchronized void addLoginListener(LoginListener listener) {
if (loginListeners == null) {
loginListeners = new CopyOnWriteArrayList<LoginListener>();
}
if (!loginListeners.contains(listener)) {
loginListeners.add(listener);
}
}
public synchronized void removeLoginListener(LoginListener listener) {
if (loginListeners != null) {
loginListeners.remove(listener);
}
}
private String getCurrentUser() {
Credentials credentials = getStorageCredentials();
if (credentials != null && credentials.getPassword() != null) {
return credentials.getUsername();
}
return null;
}
private Credentials getStorageCredentials() {
ICredentialsProvider provider = getStorage().getCredentialsProvider();
if (provider == null) {
return null;
}
IStorageService service = getStorage().getService();
//return provider.hasCredentials(service) ? provider.getCredentials(service) : null;
return ((StorageService) service).getCredentials();
}
public void activate(BundleContext context, Map<?, ?> properties) {
Object serviceUrlValue = ServiceUtil.getOverridablePropertyValue(properties, STORAGE_SERVICE_URL_PROPERTY);
if (serviceUrlValue != null) {
URI serviceUri = URI.create(serviceUrlValue.toString());
String serviceName = getProperty(properties, STORAGE_SERVICE_NAME_PROPERTY, DEFAULT_STORAGE_SERVICE_NAME);
IStorageService registered = registerStorageService(serviceUri, serviceName);
setServiceUri(registered.getServiceURI());
}
String applicationToken = getProperty(properties, APPLICATION_TOKEN_PROPERTY, DEFAULT_APPLICATION_TOKEN);
this.applicationToken = applicationToken;
}
private IStorageService registerStorageService(URI serviceUri, String serviceName) {
customStorageService = null;
URI normalizedUri = normalizePath(serviceUri);
URI denormalizedUri = normalizePath(serviceUri, false);
IStorageService service = IStorageService.Registry.INSTANCE.getService(normalizedUri);
IStorageService extraService = IStorageService.Registry.INSTANCE.getService(denormalizedUri);
if (service == null) {
if (extraService != null) {
return extraService;
}
customStorageService = IStorageService.Registry.INSTANCE.addService(serviceName, normalizedUri);
service = customStorageService;
return service;
}
if (extraService != null && extraService != service) {
service = cleanupExtraStorageService(service, extraService);
}
return service;
}
private static IStorageService cleanupExtraStorageService(IStorageService service, IStorageService extraService) {
IStorageService.Dynamic removeDynamic;
IStorageService keepService;
if (extraService instanceof IStorageService.Dynamic) {
removeDynamic = (IStorageService.Dynamic) extraService;
keepService = service;
} else if (service instanceof IStorageService.Dynamic) {
removeDynamic = (IStorageService.Dynamic) service;
keepService = extraService;
} else {
return service;
}
if (removeDynamic instanceof StorageService && keepService instanceof StorageService) {
StorageService removeImpl = (StorageService) removeDynamic;
StorageService keepImpl = (StorageService) keepService;
ISecurePreferences removeSecurePreferences = removeImpl.getSecurePreferences();
ISecurePreferences keepSecurePreferences = keepImpl.getSecurePreferences();
try {
copySecurePreferences(removeSecurePreferences, keepSecurePreferences);
} catch (Exception e) {
MarketplaceClientCore.error(NLS.bind("Failed to migrate secure storage values from {0} to {1}",
removeDynamic.getServiceURI(), keepService.getServiceURI()), e);
}
}
removeDynamic.remove();
return keepService;
}
private static void copySecurePreferences(ISecurePreferences source, ISecurePreferences target)
throws StorageException, IOException {
Set<String> sourceKeys = new HashSet<String>(Arrays.asList(source.keys()));
Set<String> targetKeys = new HashSet<String>(Arrays.asList(target.keys()));
boolean changed = false;
for (String key : MIGRATE_SECURE_STORAGE_KEYS) {
if (sourceKeys.contains(key) && !targetKeys.contains(key)) {
boolean encrypted = source.isEncrypted(key);
target.put(key, source.get(key, null), encrypted);
changed = true;
}
}
if (changed) {
target.flush();
}
}
private static URI normalizePath(URI uri) {
return normalizePath(uri, true);
}
private static URI normalizePath(URI uri, boolean trailingSlash) {
if (uri.isOpaque()) {
return uri;
}
String path = uri.getPath();
String normalizedPath;
if (path == null) {
if (trailingSlash) {
normalizedPath = "/"; //$NON-NLS-1$
} else {
return uri;
}
} else if (path.endsWith("/")) { //$NON-NLS-1$
if (trailingSlash) {
return uri;
} else {
normalizedPath = path.length() == 1 ? null : path.substring(0, path.length() - 1);
}
} else {
if (trailingSlash) {
normalizedPath = path + "/"; //$NON-NLS-1$
} else {
return uri;
}
}
try {
return new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), normalizedPath,
uri.getQuery(), uri.getFragment());
} catch (URISyntaxException e) {
return uri;
}
}
private static String getProperty(Map<?, ?> properties, String key, String defaultValue) {
Object value = properties.get(key);
if (value == null) {
return defaultValue;
}
return value.toString();
}
public void deactivate() {
if (customStorageService != null) {
customStorageService.remove();
}
customStorageService = null;
}
}