blob: 97e88007e6c90d4e8130a7ef0e8e6d84c4b9d021 [file] [log] [blame]
/*
* Copyright (c) 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.userstorage.internal;
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.util.IOUtil.TeeInputStream;
import org.eclipse.userstorage.internal.util.StringUtil;
import org.eclipse.userstorage.spi.ISettings;
import org.eclipse.userstorage.spi.StorageCache;
import org.eclipse.userstorage.util.BadApplicationTokenException;
import org.eclipse.userstorage.util.ConflictException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;
/**
* @author Eike Stepper
*/
public final class Storage implements IStorage
{
private final String applicationToken;
private final StorageFactory factory;
private final InternalStorageCache cache;
private final Map<String, Blob> blobs = new WeakHashMap<String, Blob>();
private StorageService service;
public Storage(StorageFactory factory, String applicationToken, InternalStorageCache cache) throws BadApplicationTokenException
{
this.applicationToken = BadApplicationTokenException.validate(applicationToken);
this.factory = factory;
this.cache = cache;
}
@Override
public String getApplicationToken()
{
return applicationToken;
}
@Override
public StorageCache getCache()
{
return (StorageCache)cache;
}
@Override
public StorageService getService()
{
return service;
}
@Override
public synchronized void setService(IStorageService service)
{
if (service != this.service)
{
disposeBlobs();
this.service = (StorageService)service;
setPreferredServiceURI(service.getServiceURI().toString());
if (cache != null)
{
cache.setService(service);
}
}
}
@Override
public synchronized IBlob getBlob(String key)
{
Blob blob = blobs.get(key);
if (blob == null)
{
Map<String, String> properties = new HashMap<String, String>();
if (cache != null)
{
try
{
cache.internalLoadProperties(applicationToken, key, properties);
}
catch (IOException ex)
{
properties.clear();
Activator.log(ex);
}
}
blob = new Blob(this, key, properties);
blobs.put(key, blob);
}
return blob;
}
public void setETag(String key, Map<String, String> properties, String eTag)
{
if (StringUtil.isEmpty(eTag))
{
properties.remove(Blob.ETAG);
}
else
{
properties.put(Blob.ETAG, eTag);
}
if (cache != null)
{
try
{
if (!StringUtil.isEmpty(eTag))
{
Map<String, String> cacheProperties = new HashMap<String, String>();
cache.loadProperties(applicationToken, key, cacheProperties);
String cacheETag = cacheProperties.get(Blob.ETAG);
if (eTag.equals(cacheETag))
{
// Don't delete blob from cache because it has the new ETag.
return;
}
}
cache.internalDelete(applicationToken, key);
}
catch (IOException ex)
{
Activator.log(ex);
}
}
}
public InputStream retrieveBlob(String key, Map<String, String> properties) throws IOException
{
InputStream contents = service.retrieveBlob(applicationToken, key, properties, cache != null);
if (cache != null)
{
if (contents == Blob.NOT_MODIFIED)
{
return cache.internalGetInputStream(applicationToken, key);
}
if (contents == null)
{
cache.internalDelete(applicationToken, key);
return null;
}
OutputStream output = cache.internalGetOutputStream(applicationToken, key, properties);
return new TeeInputStream(contents, output);
}
return contents;
}
public boolean updateBlob(String key, Map<String, String> properties, InputStream in) throws IOException, ConflictException
{
if (cache != null)
{
OutputStream output = cache.internalGetOutputStream(applicationToken, key, properties);
in = new TeeInputStream(in, output);
}
boolean created = service.updateBlob(applicationToken, key, properties, in);
if (cache != null)
{
cache.internalSaveProperties(applicationToken, key, properties);
}
return created;
}
@Override
public String toString()
{
return service + " (" + applicationToken + ")";
}
private void setPreferredServiceURI(String serviceURI)
{
try
{
ISettings settings = factory.getSettings();
settings.setValue(applicationToken, serviceURI);
}
catch (Exception ex)
{
Activator.log(ex);
}
}
private void disposeBlobs()
{
for (Blob blob : blobs.values())
{
blob.dispose();
}
blobs.clear();
if (cache != null)
{
try
{
for (Iterator<String> it = cache.getKeys(applicationToken); it.hasNext();)
{
String key = it.next();
try
{
cache.delete(applicationToken, key);
}
catch (Exception ex)
{
Activator.log(ex);
}
}
}
catch (Exception ex)
{
Activator.log(ex);
}
}
}
}