Search in sources :

Example 11 with StoreManager

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);
    }
}
Also used : ParsedMessageDataSource(com.zimbra.cs.mime.ParsedMessageDataSource) StagedBlob(com.zimbra.cs.store.StagedBlob) Rfc822ValidationInputStream(com.zimbra.common.mime.Rfc822ValidationInputStream) CopyInputStream(com.zimbra.common.util.CopyInputStream) InputStream(java.io.InputStream) SaveChat(com.zimbra.cs.redolog.op.SaveChat) CreateChat(com.zimbra.cs.redolog.op.CreateChat) SaveChat(com.zimbra.cs.redolog.op.SaveChat) RefreshMountpoint(com.zimbra.cs.redolog.op.RefreshMountpoint) TargetConstraint(com.zimbra.cs.mailbox.MailItem.TargetConstraint) CreateMountpoint(com.zimbra.cs.redolog.op.CreateMountpoint) StoreManager(com.zimbra.cs.store.StoreManager)

Example 12 with StoreManager

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);
    }
}
Also used : StagedBlob(com.zimbra.cs.store.StagedBlob) NormalizedTags(com.zimbra.cs.mailbox.Tag.NormalizedTags) MailboxBlob(com.zimbra.cs.store.MailboxBlob) Rfc822ValidationInputStream(com.zimbra.common.mime.Rfc822ValidationInputStream) CopyInputStream(com.zimbra.common.util.CopyInputStream) InputStream(java.io.InputStream) RefreshMountpoint(com.zimbra.cs.redolog.op.RefreshMountpoint) TargetConstraint(com.zimbra.cs.mailbox.MailItem.TargetConstraint) CreateMountpoint(com.zimbra.cs.redolog.op.CreateMountpoint) StoreManager(com.zimbra.cs.store.StoreManager) ParsedMessageDataSource(com.zimbra.cs.mime.ParsedMessageDataSource) CreateChat(com.zimbra.cs.redolog.op.CreateChat) NormalizedTags(com.zimbra.cs.mailbox.Tag.NormalizedTags) SaveChat(com.zimbra.cs.redolog.op.SaveChat) CreateChat(com.zimbra.cs.redolog.op.CreateChat) AlterItemTag(com.zimbra.cs.redolog.op.AlterItemTag) CreateTag(com.zimbra.cs.redolog.op.CreateTag) DbTag(com.zimbra.cs.db.DbTag)

Example 13 with StoreManager

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();
    }
}
Also used : DeleteMailbox(com.zimbra.cs.redolog.op.DeleteMailbox) IOException(java.io.IOException) DbConnection(com.zimbra.cs.db.DbPool.DbConnection) StoreManager(com.zimbra.cs.store.StoreManager)

Example 14 with StoreManager

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);
                }
            }
        }
    }
}
Also used : StoreIncomingBlob(com.zimbra.cs.redolog.op.StoreIncomingBlob) StagedBlob(com.zimbra.cs.store.StagedBlob) MailboxBlob(com.zimbra.cs.store.MailboxBlob) Blob(com.zimbra.cs.store.Blob) MailboxBlob(com.zimbra.cs.store.MailboxBlob) AllAccountsRedoCommitCallback(com.zimbra.cs.session.AllAccountsRedoCommitCallback) AccountServiceException(com.zimbra.cs.account.AccountServiceException) IOException(java.io.IOException) NoSuchItemException(com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException) MessageChannelException(com.zimbra.cs.iochannel.MessageChannelException) ServiceException(com.zimbra.common.service.ServiceException) UnsupportedEncodingException(java.io.UnsupportedEncodingException) DbConnection(com.zimbra.cs.db.DbPool.DbConnection) StoreManager(com.zimbra.cs.store.StoreManager) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) RedoableOp(com.zimbra.cs.redolog.op.RedoableOp) PendingDelete(com.zimbra.cs.mailbox.MailItem.PendingDelete)

Example 15 with StoreManager

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);
    }
}
Also used : StoreIncomingBlob(com.zimbra.cs.redolog.op.StoreIncomingBlob) StagedBlob(com.zimbra.cs.store.StagedBlob) MailboxBlob(com.zimbra.cs.store.MailboxBlob) Blob(com.zimbra.cs.store.Blob) StagedBlob(com.zimbra.cs.store.StagedBlob) Rfc822ValidationInputStream(com.zimbra.common.mime.Rfc822ValidationInputStream) CopyInputStream(com.zimbra.common.util.CopyInputStream) InputStream(java.io.InputStream) CalendarPartInfo(com.zimbra.cs.mime.ParsedMessage.CalendarPartInfo) AccountServiceException(com.zimbra.cs.account.AccountServiceException) IOException(java.io.IOException) NoSuchItemException(com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException) MessageChannelException(com.zimbra.cs.iochannel.MessageChannelException) ServiceException(com.zimbra.common.service.ServiceException) UnsupportedEncodingException(java.io.UnsupportedEncodingException) StoreManager(com.zimbra.cs.store.StoreManager)

Aggregations

StoreManager (com.zimbra.cs.store.StoreManager)29 MailboxBlob (com.zimbra.cs.store.MailboxBlob)17 StagedBlob (com.zimbra.cs.store.StagedBlob)16 IOException (java.io.IOException)14 InputStream (java.io.InputStream)11 Blob (com.zimbra.cs.store.Blob)10 Mailbox (com.zimbra.cs.mailbox.Mailbox)9 ParsedMessage (com.zimbra.cs.mime.ParsedMessage)9 Rfc822ValidationInputStream (com.zimbra.common.mime.Rfc822ValidationInputStream)6 ServiceException (com.zimbra.common.service.ServiceException)6 CopyInputStream (com.zimbra.common.util.CopyInputStream)6 TargetConstraint (com.zimbra.cs.mailbox.MailItem.TargetConstraint)5 CreateMountpoint (com.zimbra.cs.redolog.op.CreateMountpoint)5 Test (org.junit.Test)5 DbConnection (com.zimbra.cs.db.DbPool.DbConnection)4 RefreshMountpoint (com.zimbra.cs.redolog.op.RefreshMountpoint)4 ArrayList (java.util.ArrayList)4 AccountServiceException (com.zimbra.cs.account.AccountServiceException)3 DbMailItem (com.zimbra.cs.db.DbMailItem)3 ParsedMessageDataSource (com.zimbra.cs.mime.ParsedMessageDataSource)3