372 lines
9.2 KiB
Java
372 lines
9.2 KiB
Java
/**
|
|
* Author: Timothy Prepscius
|
|
* License: GPLv3 Affero + keep my name in the code!
|
|
*/
|
|
package mail.client;
|
|
|
|
import core.callback.Callback;
|
|
import core.callback.CallbackDefault;
|
|
import core.callbacks.CountDown;
|
|
import core.callbacks.Split;
|
|
import core.callbacks.SuccessFailure;
|
|
import core.connector.async.AsyncStoreConnector;
|
|
import core.constants.ConstantsClient;
|
|
import core.constants.ConstantsClient;
|
|
import core.constants.ConstantsSettings;
|
|
import core.crypt.CryptorSeed;
|
|
import core.util.LogNull;
|
|
import core.util.LogOut;
|
|
import mail.client.cache.Cache;
|
|
import mail.client.cache.ID;
|
|
import mail.client.cache.IndexedCache;
|
|
import mail.client.cache.IndexedCacheSerializer;
|
|
import mail.client.cache.ItemCacheFactory;
|
|
import mail.client.cache.ItemSerializer;
|
|
import mail.client.cache.JSON;
|
|
import mail.client.cache.StoreFactory;
|
|
import mail.client.cache.StoreLibrary;
|
|
import mail.client.cache.Type;
|
|
import mail.client.model.Attachments;
|
|
import mail.client.model.Body;
|
|
import mail.client.model.Conversation;
|
|
import mail.client.model.Folder;
|
|
import mail.client.model.FolderDefinition;
|
|
import mail.client.model.Header;
|
|
import mail.client.model.Model;
|
|
import mail.client.model.ModelFactory;
|
|
import mail.client.model.Mail;
|
|
import mail.client.model.ModelSerializer;
|
|
import mail.client.model.PublicKey;
|
|
import mail.client.model.PublicKeyRing;
|
|
import mail.client.model.Settings;
|
|
|
|
public class CacheManager extends Servent<Master>
|
|
{
|
|
static LogNull log = new LogNull(CacheManager.class);
|
|
|
|
Cache masterCache;
|
|
IndexedCache cacheMail;
|
|
IndexedCache cacheConversation;
|
|
IndexedCache cacheFolder;
|
|
IndexedCache cacheKeys;
|
|
|
|
PublicKeyRing keyRing;
|
|
Settings settings;
|
|
|
|
boolean isCaching = false;
|
|
|
|
ModelFactory itemFactory;
|
|
|
|
StoreLibrary library;
|
|
AsyncStoreConnector connector;
|
|
|
|
public CacheManager (CryptorSeed cryptorSeed, AsyncStoreConnector connector)
|
|
{
|
|
this.connector = connector;
|
|
this.itemFactory = new ModelFactory(this);
|
|
library = new StoreLibrary(cryptorSeed, new StoreFactory(85 * 1024), connector);
|
|
}
|
|
|
|
public void onModelDirty ()
|
|
{
|
|
log.debug("markDirty");
|
|
master.getEventPropagator().signal(Events.CacheDirty, (Object[])null);
|
|
}
|
|
|
|
public void start ()
|
|
{
|
|
log.debug("start");
|
|
|
|
JSON json = getMaster().getJSON();
|
|
|
|
this.masterCache = new Cache(
|
|
null,
|
|
new MasterCacheSerializer(json),
|
|
library.instantiate("I", null, false) // false because I'm manually initiating below .start(...)
|
|
);
|
|
|
|
this.settings = new Settings(this);
|
|
this.settings.setId(Constants.SETTINGS_ID);
|
|
this.masterCache.link(settings);
|
|
|
|
this.keyRing = new PublicKeyRing(this);
|
|
this.keyRing.setId(Constants.KEYRING_ID);
|
|
this.masterCache.link(keyRing);
|
|
|
|
ItemSerializer itemSerializer = new ModelSerializer(json);
|
|
|
|
this.cacheMail = new IndexedCache(
|
|
new ItemCacheFactory ("M", library, itemFactory, itemSerializer)
|
|
);
|
|
this.cacheMail.setId(Constants.MAIL_ID);
|
|
masterCache.link(cacheMail);
|
|
|
|
this.cacheConversation = new IndexedCache(
|
|
new ItemCacheFactory ("C", library, itemFactory, itemSerializer)
|
|
);
|
|
this.cacheConversation.setId(Constants.CONVERSATION_ID);
|
|
masterCache.link(cacheConversation);
|
|
|
|
this.cacheFolder = new IndexedCache(
|
|
new ItemCacheFactory ("F", library, itemFactory, itemSerializer)
|
|
);
|
|
this.cacheFolder.setId(Constants.FOLDER_ID);
|
|
masterCache.link(cacheFolder);
|
|
|
|
this.cacheKeys = new IndexedCache(
|
|
new ItemCacheFactory ("K", library, itemFactory, itemSerializer)
|
|
);
|
|
this.cacheKeys.setId(Constants.KEY_ID);
|
|
masterCache.link(cacheKeys);
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
Callback countDown =
|
|
new CountDown(
|
|
5,
|
|
getMaster().getEventPropagator().signal_(Events.Initialize_IndexedCacheLoadComplete)
|
|
);
|
|
|
|
// some how I need to watch when the indexCaches have been loaded
|
|
settings.apply(new Split(countDown));
|
|
cacheMail.apply(new Split(countDown));
|
|
cacheConversation.apply(new Split(countDown));
|
|
cacheFolder.apply(new Split(countDown));
|
|
cacheKeys.apply(new Split(countDown));
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
library.start(
|
|
new SuccessFailure(
|
|
getMaster().getEventPropagator().signal_(Events.Initialize_IndexedCacheAcquired, (Object[])null),
|
|
getMaster().getEventPropagator().signal_(Events.Initialize_IndexedCacheLoadFailed, (Object[])null)
|
|
)
|
|
);
|
|
}
|
|
|
|
public void deserializeIndexCaches ()
|
|
{
|
|
log.debug("deserializeIndexCaches");
|
|
}
|
|
|
|
public void firstRunInitialization ()
|
|
{
|
|
log.debug("firstRunInitialization");
|
|
|
|
// we create the cache the root folders are in
|
|
cacheFolder.newCache(Indexer.KnownFolderIds.RootCache);
|
|
|
|
cacheMail.markCreate();
|
|
cacheConversation.markCreate();
|
|
cacheFolder.markCreate();
|
|
cacheKeys.markCreate();
|
|
masterCache.markCreate();
|
|
|
|
settings.markCreate();
|
|
settings.set(Settings.VERSION, Settings.CURRENT_VERSION);
|
|
}
|
|
|
|
public Settings getSettings ()
|
|
{
|
|
return settings;
|
|
}
|
|
|
|
public String createUIDL (ID id)
|
|
{
|
|
return "<" + id + ConstantsClient.ATHOST + ">";
|
|
}
|
|
|
|
public Mail newMail(Header header, Body body, Attachments attachments) throws Exception
|
|
{
|
|
log.debug("newMail");
|
|
|
|
|
|
Mail mail = (Mail)itemFactory.instantiate(Type.Mail);
|
|
cacheMail.put(mail);
|
|
|
|
mail.setHeader(header);
|
|
|
|
// if there is no UIDL we supply one, based off of the external key
|
|
if (header.getUIDL()==null)
|
|
header.setUIDL(createUIDL(mail.getId()));
|
|
|
|
mail.setBody(body);
|
|
mail.setAttachments(attachments);
|
|
|
|
master.getEventPropagator().signalOnce(Events.NewMail, mail);
|
|
return mail;
|
|
}
|
|
|
|
public void deleteMail(Mail mail)
|
|
{
|
|
log.debug("deleteMail");
|
|
master.getEventPropagator().signalOnce(Events.DeleteMail, mail);
|
|
mail.markDeleted();
|
|
}
|
|
|
|
public Conversation newConversation (Mail mail) throws Exception
|
|
{
|
|
log.debug("newConversation");
|
|
|
|
Conversation conversation = (Conversation)itemFactory.instantiate(Type.Conversation);
|
|
cacheConversation.put(conversation);
|
|
|
|
conversation.addItem(mail);
|
|
master.getEventPropagator().signalOnce(Events.NewConversation, conversation);
|
|
|
|
return conversation;
|
|
}
|
|
|
|
public void deleteConversation(Conversation conversation)
|
|
{
|
|
log.debug("deleteConversation");
|
|
master.getEventPropagator().signalOnce(Events.DeleteConversation, conversation);
|
|
conversation.markDeleted();
|
|
}
|
|
|
|
public Folder newFolder(Type type, FolderDefinition folderDefinition)
|
|
{
|
|
log.debug("newFolder", type);
|
|
Folder folder = (Folder)itemFactory.instantiate(type);
|
|
cacheFolder.put(folder);
|
|
|
|
folder.setFolderDefinition(folderDefinition);
|
|
master.getEventPropagator().signalOnce(Events.NewFolder, folder);
|
|
|
|
return folder;
|
|
}
|
|
|
|
public Folder linkFolder(ID id, Type type, FolderDefinition folderDefinition)
|
|
{
|
|
log.debug("newFolder", type, id);
|
|
Folder folder = (Folder)itemFactory.instantiate(type);
|
|
cacheFolder.link(id, folder);
|
|
|
|
folder.setFolderDefinition(folderDefinition);
|
|
master.getEventPropagator().signalOnce(Events.NewFolder, folder);
|
|
|
|
return folder;
|
|
}
|
|
|
|
public Mail getMail(ID uid)
|
|
{
|
|
return (Mail) cacheMail.getAndAcquire(Type.Mail, uid);
|
|
}
|
|
|
|
public void putMail(Mail m)
|
|
{
|
|
cacheMail.put(m);
|
|
}
|
|
|
|
public Conversation getConversation(ID uid)
|
|
{
|
|
return (Conversation) cacheConversation.getAndAcquire(Type.Conversation, uid);
|
|
}
|
|
|
|
public void putConversation(Conversation c)
|
|
{
|
|
cacheConversation.put(c);
|
|
}
|
|
|
|
public Folder getFolder(Type type, ID id)
|
|
{
|
|
return (Folder)cacheFolder.getAndAcquire(type, id);
|
|
}
|
|
|
|
public void putFolder(Folder f)
|
|
{
|
|
cacheFolder.put(f);
|
|
}
|
|
|
|
public void putKey(PublicKey k)
|
|
{
|
|
cacheKeys.put(k);
|
|
}
|
|
|
|
public PublicKey getKey (ID id)
|
|
{
|
|
return (PublicKey)cacheKeys.getAndAcquire(Type.PublicKey, id);
|
|
}
|
|
|
|
public boolean isFullyCached ()
|
|
{
|
|
return (!library.hasDirtyChildren() && !masterCache.hasDirtyChildren());
|
|
}
|
|
|
|
public Callback onCacheFinished_ ()
|
|
{
|
|
return new CallbackDefault() {
|
|
public void onSuccess(Object... arguments) throws Exception {
|
|
isCaching = false;
|
|
if (isFullyCached())
|
|
master.getEventPropagator().signal(Events.CacheClean, (Object[])null);
|
|
|
|
master.getEventPropagator().signal(Events.CacheSuccess, (Object[])null);
|
|
master.getEventPropagator().signal(Events.CacheEnd, (Object[])null);
|
|
|
|
next(arguments);
|
|
}
|
|
|
|
public void onFailure (Exception e)
|
|
{
|
|
isCaching = false;
|
|
master.getEventPropagator().signal(Events.CacheFailure, e);
|
|
master.getEventPropagator().signal(Events.CacheEnd, (Object[])null);
|
|
|
|
next(e);
|
|
}
|
|
};
|
|
}
|
|
|
|
public void flush ()
|
|
{
|
|
if (isCaching)
|
|
return;
|
|
|
|
if (getMaster().getArrivalsMonitor().isChecking())
|
|
return;
|
|
|
|
if (isFullyCached())
|
|
return;
|
|
|
|
doFlush();
|
|
}
|
|
|
|
protected void doFlush ()
|
|
{
|
|
log.debug("doFlush");
|
|
|
|
isCaching = true;
|
|
master.getEventPropagator().signal(Events.CacheBegin, (Object[])null);
|
|
|
|
masterCache.debug_()
|
|
.addCallback(masterCache.flush_())
|
|
.addCallback(masterCache.checkClean_())
|
|
.addCallback(masterCache.debug_())
|
|
.addCallback(library.flush_())
|
|
.addCallback(masterCache.debug_())
|
|
.addCallback(onCacheFinished_())
|
|
.invoke();
|
|
}
|
|
|
|
public Callback update_ ()
|
|
{
|
|
return library.update_(false);
|
|
}
|
|
|
|
public void update ()
|
|
{
|
|
update_().invoke();
|
|
}
|
|
|
|
public void debug ()
|
|
{
|
|
masterCache.debug_().invoke();
|
|
}
|
|
|
|
public void onSettingsChanged (Settings settings)
|
|
{
|
|
getMaster().getIdentity().setName(settings.get(ConstantsSettings.USERNAME));
|
|
}
|
|
}
|