use of com.zimbra.cs.store.StoreManager in project zm-mailbox by Zimbra.
the class Mailbox method updateChat.
public Chat updateChat(OperationContext octxt, ParsedMessage pm, int id) throws IOException, ServiceException {
if (pm == null) {
throw ServiceException.INVALID_REQUEST("null ParsedMessage when updating chat " + id + " in mailbox " + mId, null);
}
// write the chat content directly to the mailbox's blob staging area
StoreManager sm = StoreManager.getInstance();
StagedBlob staged;
InputStream is = pm.getRawInputStream();
try {
staged = sm.stage(is, this);
} finally {
ByteUtil.closeStream(is);
}
String digest = staged.getDigest();
int size = (int) staged.getSize();
SaveChat redoRecorder = new SaveChat(mId, id, digest, size, -1, 0, null);
boolean success = false;
try {
beginTransaction("saveChat", octxt, redoRecorder);
SaveChat redoPlayer = (SaveChat) currentChange().getRedoPlayer();
redoRecorder.setMessageBodyInfo(new ParsedMessageDataSource(pm), size);
Chat chat = (Chat) getItemById(id, MailItem.Type.CHAT);
if (!chat.isMutable()) {
throw MailServiceException.IMMUTABLE_OBJECT(id);
}
if (!checkItemChangeID(chat)) {
throw MailServiceException.MODIFY_CONFLICT();
}
// content changed, so we're obliged to change the IMAP uid
int imapID = getNextItemId(redoPlayer == null ? ID_AUTO_INCREMENT : redoPlayer.getImapId());
redoRecorder.setImapId(imapID);
// update the content and increment the revision number
chat.setContent(staged, pm);
// NOTE: msg is now uncached (will this cause problems during commit/reindex?)
index.add(chat);
success = true;
return chat;
} finally {
endTransaction(success);
sm.quietDelete(staged);
}
}
use of com.zimbra.cs.store.StoreManager in project zm-mailbox by Zimbra.
the class Mailbox method createChat.
public Chat createChat(OperationContext octxt, ParsedMessage pm, int folderId, int flags, String[] tags) throws IOException, ServiceException {
if (pm == null) {
throw ServiceException.INVALID_REQUEST("null ParsedMessage when adding chat to mailbox " + mId, null);
}
// write the chat content directly to the mailbox's blob staging area
StoreManager sm = StoreManager.getInstance();
StagedBlob staged;
InputStream is = pm.getRawInputStream();
try {
staged = sm.stage(is, this);
} finally {
ByteUtil.closeStream(is);
}
String digest = staged.getDigest();
int size = (int) staged.getSize();
CreateChat redoRecorder = new CreateChat(mId, digest, size, folderId, flags, tags);
boolean success = false;
try {
beginTransaction("createChat", octxt, redoRecorder);
Tag.NormalizedTags ntags = new Tag.NormalizedTags(this, tags);
CreateChat redoPlayer = (octxt == null ? null : (CreateChat) octxt.getPlayer());
redoRecorder.setMessageBodyInfo(new ParsedMessageDataSource(pm), size);
int itemId = getNextItemId(redoPlayer == null ? ID_AUTO_INCREMENT : redoPlayer.getMessageId());
Chat chat = Chat.create(itemId, getFolderById(folderId), pm, staged, false, flags, ntags);
redoRecorder.setMessageId(chat.getId());
MailboxBlob mblob = sm.link(staged, this, itemId, getOperationChangeID());
markOtherItemDirty(mblob);
// when we created the Chat, we used the staged locator/size/digest;
// make sure that data actually matches the final blob in the store
chat.updateBlobData(mblob);
index.add(chat);
success = true;
return chat;
} finally {
endTransaction(success);
sm.quietDelete(staged);
}
}
use of com.zimbra.cs.store.StoreManager in project zm-mailbox by Zimbra.
the class Mailbox method deleteMailbox.
public void deleteMailbox(DeleteBlobs deleteBlobs) throws ServiceException {
StoreManager sm = StoreManager.getInstance();
boolean deleteStore = deleteBlobs == DeleteBlobs.ALWAYS || (deleteBlobs == DeleteBlobs.UNLESS_CENTRALIZED && !sm.supports(StoreFeature.CENTRALIZED));
SpoolingCache<MailboxBlob.MailboxBlobInfo> blobs = null;
lock.lock();
try {
// first, throw the mailbox into maintenance mode
// (so anyone else with a cached reference to the Mailbox can't use it)
MailboxMaintenance maint = null;
try {
maint = MailboxManager.getInstance().beginMaintenance(mData.accountId, mId);
} catch (MailServiceException e) {
// there may be other files that still need to be cleaned up.
if (!MailServiceException.WRONG_MAILBOX.equals(e.getCode())) {
throw e;
}
}
DeleteMailbox redoRecorder = new DeleteMailbox(mId);
boolean needRedo = needRedo(null, redoRecorder);
boolean success = false;
try {
beginTransaction("deleteMailbox", null, redoRecorder);
if (needRedo) {
redoRecorder.log();
}
if (deleteStore && !sm.supports(StoreManager.StoreFeature.BULK_DELETE)) {
blobs = DbMailItem.getAllBlobs(this);
}
try {
// Remove the mime messages from MessageCache
if (deleteStore) {
DbMailItem.visitAllBlobDigests(this, new MessageCachePurgeCallback());
}
// remove all the relevant entries from the database
DbConnection conn = getOperationConnection();
DbMailbox.clearMailboxContent(this);
DbMailbox.deleteMailbox(conn, this);
DbVolumeBlobs.deleteBlobRef(conn, this);
// Remove all data related to this mailbox from memcached, so the data doesn't
// get used by another user later by mistake if/when mailbox id gets reused.
MemcachedCacheManager.purgeMailbox(this);
success = true;
} finally {
// commit the DB transaction before touching the store! (also ends the operation)
endTransaction(success);
}
if (success) {
// remove all traces of the mailbox from the Mailbox cache
// (so anyone asking for the Mailbox gets NO_SUCH_MBOX or creates a fresh new empty one with a different id)
MailboxManager.getInstance().markMailboxDeleted(this);
// attempt to nuke the store and index
try {
index.deleteIndex();
} catch (IOException iox) {
ZimbraLog.store.warn("Unable to delete index data", iox);
}
if (deleteStore) {
try {
sm.deleteStore(this, blobs);
} catch (IOException iox) {
ZimbraLog.store.warn("Unable to delete message data", iox);
}
}
}
} finally {
if (needRedo) {
if (success) {
redoRecorder.commit();
} else {
redoRecorder.abort();
}
}
if (maint != null) {
if (success) {
// twiddle the mailbox lock [must be the last command of this function!]
// (so even *we* can't access this Mailbox going forward)
maint.markUnavailable();
} else {
// end the maintenance if the delete is not successful.
MailboxManager.getInstance().endMaintenance(maint, success, true);
}
}
if (blobs != null) {
blobs.cleanup();
}
}
} finally {
lock.release();
}
}
use of com.zimbra.cs.store.StoreManager in project zm-mailbox by Zimbra.
the class Mailbox method endTransaction.
/**
* Be very careful when changing code in this method. The order of almost
* every line of code is important to ensure correct redo logging and crash
* recovery.
*
* @param success true to commit the transaction, false to rollback
* @throws ServiceException error
*/
protected void endTransaction(boolean success) throws ServiceException {
assert !Thread.holdsLock(this) : "Use MailboxLock";
if (lock.isUnlocked()) {
ZimbraLog.mailbox.warn("transaction canceled because of lock failure");
assert (!success);
return;
}
// blob and index to delete
PendingDelete deletes = null;
// blob to delete for failure cases
List<Object> rollbackDeletes = null;
try {
if (!currentChange().isActive()) {
// would like to throw here, but it might cover another
// exception...
ZimbraLog.mailbox.warn("cannot end a transaction when not inside a transaction", new Exception());
return;
}
if (!currentChange().endChange()) {
return;
}
ServiceException exception = null;
if (success) {
List<IndexItemEntry> indexItems = currentChange().indexItems;
if (!indexItems.isEmpty()) {
assert (currentChange().writeChange);
//TODO: See bug 15072 - we need to clear mCurrentChange.indexItems (it is stored in a temporary) here,
// just in case item.reindex() recurses into a new transaction...
currentChange().indexItems = new ArrayList<IndexItemEntry>();
index.add(indexItems);
}
// update mailbox size, folder unread/message counts
try {
snapshotCounts();
} catch (ServiceException e) {
exception = e;
success = false;
}
}
DbConnection conn = currentChange().conn;
// transaction, so no redo cleanup is necessary.
if (!success) {
DbPool.quietRollback(conn);
rollbackDeletes = rollbackCache(currentChange());
if (exception != null) {
throw exception;
}
return;
}
RedoableOp redoRecorder = currentChange().recorder;
boolean needRedo = needRedo(currentChange().octxt, redoRecorder);
// Log the change redo record for main transaction.
if (redoRecorder != null && needRedo) {
redoRecorder.log(true);
}
boolean dbCommitSuccess = false;
try {
// Commit the main transaction in database.
if (conn != null) {
try {
conn.commit();
} catch (Throwable t) {
// Any exception during database commit is a disaster
// because we don't know if the change is committed or
// not. Force the server to abort. Next restart will
// redo the operation to ensure the change is made and
// committed. (bug 2121)
Zimbra.halt("Unable to commit database transaction. Forcing server to abort.", t);
}
}
dbCommitSuccess = true;
} finally {
if (!dbCommitSuccess) {
// recovery will try to redo the operation.
if (needRedo) {
if (redoRecorder != null) {
redoRecorder.abort();
}
}
DbPool.quietRollback(conn);
rollbackDeletes = rollbackCache(currentChange());
return;
}
}
if (needRedo) {
// case would result in a redo error, and the second case would index the wrong value.
if (redoRecorder != null) {
if (currentChange().dirty != null && !currentChange().dirty.changedTypes.isEmpty()) {
// if an "all accounts" waitset is active, and this change has an appropriate type,
// then we'll need to set a commit-callback
AllAccountsRedoCommitCallback cb = AllAccountsRedoCommitCallback.getRedoCallbackIfNecessary(getAccountId(), currentChange().dirty.changedTypes);
if (cb != null) {
redoRecorder.setCommitCallback(cb);
}
}
redoRecorder.commit();
}
}
boolean changeMade = currentChange().changeId != MailboxChange.NO_CHANGE;
// keep a reference for cleanup
deletes = currentChange().deletes;
// deletes outside the lock
// We are finally done with database and redo commits. Cache update
// comes last.
commitCache(currentChange());
// down in its call stack.
if (changeMade) {
index.maybeIndexDeferredItems();
}
} finally {
lock.release();
// entail a blocking network operation
if (deletes != null) {
if (!deletes.indexIds.isEmpty()) {
// delete any index entries associated with items deleted from db
index.delete(deletes.indexIds);
}
if (deletes.blobs != null) {
// delete any blobs associated with items deleted from db/index
StoreManager sm = StoreManager.getInstance();
for (MailboxBlob blob : deletes.blobs) {
sm.quietDelete(blob);
}
}
}
if (rollbackDeletes != null) {
StoreManager sm = StoreManager.getInstance();
for (Object obj : rollbackDeletes) {
if (obj instanceof MailboxBlob) {
sm.quietDelete((MailboxBlob) obj);
} else if (obj instanceof Blob) {
sm.quietDelete((Blob) obj);
}
}
}
}
}
use of com.zimbra.cs.store.StoreManager in project zm-mailbox by Zimbra.
the class Mailbox method addMessage.
private Message addMessage(OperationContext octxt, ParsedMessage pm, int folderId, boolean noICal, int flags, String[] tags, int conversationId, String rcptEmail, Message.DraftInfo dinfo, CustomMetadata customData, DeliveryContext dctxt) throws IOException, ServiceException {
// and then actually add the message
long start = ZimbraPerf.STOPWATCH_MBOX_ADD_MSG.start();
//
if (!noICal) {
try {
CalendarPartInfo cpi = pm.getCalendarPartInfo();
if (cpi != null && CalendarItem.isAcceptableInvite(getAccount(), cpi)) {
if (ICalTok.REPLY.equals(cpi.method)) {
processICalReplies(octxt, cpi.cal, null);
} else if (ICalTok.COUNTER.equals(cpi.method)) {
processICalReplies(octxt, cpi.cal, pm.getSender());
}
}
} catch (Exception e) {
ZimbraLog.calendar.warn("Error during calendar processing. Continuing with message add", e);
}
}
// Store the incoming blob if necessary.
if (dctxt == null) {
dctxt = new DeliveryContext();
}
StoreManager sm = StoreManager.getInstance();
Blob blob = dctxt.getIncomingBlob();
boolean deleteIncoming = false;
if (blob == null) {
InputStream in = null;
try {
in = pm.getRawInputStream();
blob = sm.storeIncoming(in);
} finally {
ByteUtil.closeStream(in);
}
dctxt.setIncomingBlob(blob);
deleteIncoming = true;
}
StagedBlob staged = sm.stage(blob, this);
lock.lock();
try {
try {
return addMessageInternal(octxt, pm, folderId, noICal, flags, tags, conversationId, rcptEmail, dinfo, customData, dctxt, staged);
} finally {
if (deleteIncoming) {
sm.quietDelete(dctxt.getIncomingBlob());
}
sm.quietDelete(staged);
}
} finally {
lock.release();
ZimbraPerf.STOPWATCH_MBOX_ADD_MSG.stop(start);
}
}
Aggregations