<commit_before>/****************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one   *
 * or more contributor license agreements.  See the NOTICE file *
 * distributed with this work for additional information        *
 * regarding copyright ownership.  The ASF licenses this file   *
 * to you under the Apache License, Version 2.0 (the            *
 * "License"); you may not use this file except in compliance   *
 * with the License.  You may obtain a copy of the License at   *
 *                                                              *
 *   http://www.apache.org/licenses/LICENSE-2.0                 *
 *                                                              *
 * Unless required by applicable law or agreed to in writing,   *
 * software distributed under the License is distributed on an  *
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
 * KIND, either express or implied.  See the License for the    *
 * specific language governing permissions and limitations      *
 * under the License.                                           *
 ****************************************************************/



package org.apache.james.core;

import javax.activation.DataHandler;
import javax.mail.Address;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Flags.Flag;
import javax.mail.internet.MimeMessage;
import javax.mail.search.SearchTerm;

import org.apache.james.lifecycle.Disposable;
import org.apache.james.lifecycle.LifecycleUtil;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.Enumeration;

/**
 * This object wraps a "possibly shared" MimeMessage tracking copies and
 * automatically cloning it (if shared) when a write operation is invoked.
 */
public class MimeMessageCopyOnWriteProxy extends MimeMessage implements
        Disposable {

    /**
     * Used internally to track the reference count
     * It is important that this is static otherwise it will keep a reference to
     * the parent object.
     */
    protected static class MessageReferenceTracker {

        /**
         * reference counter
         */
        private int referenceCount = 1;
        
        /**
         * The mime message in memory
         */
        private MimeMessage wrapped = null;
        
        public MessageReferenceTracker(MimeMessage ref) {
            wrapped = ref;
        }

        protected synchronized void incrementReferenceCount() {
            /* Used to track references while debugging
            try {
                throw new Exception("incrementReferenceCount: "+(wrapped != null ? System.identityHashCode(wrapped)+"" : "null")+" ["+referenceCount+"]");
            } catch (Exception e) {
                e.printStackTrace();
            }
            */
            referenceCount++;
        }

        protected synchronized void decrementReferenceCount() {
            /* Used to track references while debugging
            try {
                throw new Exception("decrementReferenceCount: "+(wrapped != null ? System.identityHashCode(wrapped)+"" : "null")+" ["+referenceCount+"]");
            } catch (Exception e) {
                e.printStackTrace();
            }
            */
            referenceCount--;
            if (referenceCount<=0) {
                LifecycleUtil.dispose(wrapped);
                wrapped = null;
            }
        }

        protected synchronized int getReferenceCount() {
            return referenceCount;
        }

        public MimeMessage getWrapped() {
            return wrapped;
        }

    }

    protected MessageReferenceTracker refCount;

    /**
     * @param original
     *            MimeMessageWrapper
     * @throws MessagingException
     */
    public MimeMessageCopyOnWriteProxy(MimeMessage original)
            throws MessagingException {
        this(original, false);
    }

    /**
     * @param original
     *            MimeMessageSource
     * @throws MessagingException
     */
    public MimeMessageCopyOnWriteProxy(MimeMessageSource original)
            throws MessagingException {
        this(new MimeMessageWrapper(original), true);
    }

    /**
     * Private constructor providing an external reference counter.
     * 
     * @param original
     * @param writeable
     * @throws MessagingException
     */
    private MimeMessageCopyOnWriteProxy(MimeMessage original,
            boolean writeable)
            throws MessagingException {
        super(Session.getDefaultInstance(System.getProperties(), null));

        if (original instanceof MimeMessageCopyOnWriteProxy) {
            refCount = ((MimeMessageCopyOnWriteProxy) original).refCount;
        } else {
            refCount = new MessageReferenceTracker(original);
        }
        
        if (!writeable) {
            refCount.incrementReferenceCount();
        }
    }

    /**
     * Check the number of references over the MimeMessage and clone it if
     * needed before returning the reference
     * 
     * @throws MessagingException
     *             exception
     */
    protected MimeMessage getWrappedMessageForWriting() throws MessagingException {
        synchronized (refCount) {
            if (refCount.getReferenceCount() > 1) {
                refCount.decrementReferenceCount();
                refCount = new MessageReferenceTracker(new MimeMessageWrapper(refCount.getWrapped()));
            }
        }
        return refCount.getWrapped();
    }
    
    /**
     * Return wrapped mimeMessage
     * 
     * @return wrapped return the wrapped mimeMessage
     */
    public MimeMessage getWrappedMessage() {
        return refCount.getWrapped();
    }


    /**
     * @see org.apache.avalon.framework.activity.Disposable#dispose()
     */
    public synchronized void dispose() {
        if (refCount != null) {
            refCount.decrementReferenceCount();
            refCount = null;
        }<commit_msg>Use proper synchronization in MimeMessageCopyOnWriteProxy to fix possible race-condition (JAMES-900)<commit_after>/****************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one   *
 * or more contributor license agreements.  See the NOTICE file *
 * distributed with this work for additional information        *
 * regarding copyright ownership.  The ASF licenses this file   *
 * to you under the Apache License, Version 2.0 (the            *
 * "License"); you may not use this file except in compliance   *
 * with the License.  You may obtain a copy of the License at   *
 *                                                              *
 *   http://www.apache.org/licenses/LICENSE-2.0                 *
 *                                                              *
 * Unless required by applicable law or agreed to in writing,   *
 * software distributed under the License is distributed on an  *
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
 * KIND, either express or implied.  See the License for the    *
 * specific language governing permissions and limitations      *
 * under the License.                                           *
 ****************************************************************/



package org.apache.james.core;

import javax.activation.DataHandler;
import javax.mail.Address;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Flags.Flag;
import javax.mail.internet.MimeMessage;
import javax.mail.search.SearchTerm;

import org.apache.james.lifecycle.Disposable;
import org.apache.james.lifecycle.LifecycleUtil;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.Enumeration;

/**
 * This object wraps a "possibly shared" MimeMessage tracking copies and
 * automatically cloning it (if shared) when a write operation is invoked.
 */
public class MimeMessageCopyOnWriteProxy extends MimeMessage implements
        Disposable {

    /**
     * Used internally to track the reference count
     * It is important that this is static otherwise it will keep a reference to
     * the parent object.
     */
    protected static class MessageReferenceTracker {

        /**
         * reference counter
         */
        private int referenceCount = 1;
        
        /**
         * The mime message in memory
         */
        private MimeMessage wrapped = null;
        
        public MessageReferenceTracker(MimeMessage ref) {
            wrapped = ref;
        }

        protected synchronized void incrementReferenceCount() {
            /* Used to track references while debugging
            try {
                throw new Exception("incrementReferenceCount: "+(wrapped != null ? System.identityHashCode(wrapped)+"" : "null")+" ["+referenceCount+"]");
            } catch (Exception e) {
                e.printStackTrace();
            }
            */
            referenceCount++;
        }

        protected synchronized void decrementReferenceCount() {
            /* Used to track references while debugging
            try {
                throw new Exception("decrementReferenceCount: "+(wrapped != null ? System.identityHashCode(wrapped)+"" : "null")+" ["+referenceCount+"]");
            } catch (Exception e) {
                e.printStackTrace();
            }
            */
            referenceCount--;
            if (referenceCount<=0) {
                LifecycleUtil.dispose(wrapped);
                wrapped = null;
            }
        }

        protected synchronized int getReferenceCount() {
            return referenceCount;
        }

        public synchronized MimeMessage getWrapped() {
            return wrapped;
        }

    }

    protected MessageReferenceTracker refCount;

    /**
     * @param original
     *            MimeMessageWrapper
     * @throws MessagingException
     */
    public MimeMessageCopyOnWriteProxy(MimeMessage original)
            throws MessagingException {
        this(original, false);
    }

    /**
     * @param original
     *            MimeMessageSource
     * @throws MessagingException
     */
    public MimeMessageCopyOnWriteProxy(MimeMessageSource original)
            throws MessagingException {
        this(new MimeMessageWrapper(original), true);
    }

    /**
     * Private constructor providing an external reference counter.
     * 
     * @param original
     * @param writeable
     * @throws MessagingException
     */
    private MimeMessageCopyOnWriteProxy(MimeMessage original,
            boolean writeable)
            throws MessagingException {
        super(Session.getDefaultInstance(System.getProperties(), null));

        if (original instanceof MimeMessageCopyOnWriteProxy) {
            refCount = ((MimeMessageCopyOnWriteProxy) original).refCount;
        } else {
            refCount = new MessageReferenceTracker(original);
        }
        
        if (!writeable) {
            refCount.incrementReferenceCount();
        }
    }

    /**
     * Check the number of references over the MimeMessage and clone it if
     * needed before returning the reference
     * 
     * @throws MessagingException
     *             exception
     */
    protected synchronized MimeMessage getWrappedMessageForWriting() throws MessagingException {
        if (refCount.getReferenceCount() > 1) {
            refCount.decrementReferenceCount();
            refCount = new MessageReferenceTracker(new MimeMessageWrapper(refCount.getWrapped()));
        }
        return refCount.getWrapped();
    }
    
    /**
     * Return wrapped mimeMessage
     * 
     * @return wrapped return the wrapped mimeMessage
     */
    public synchronized MimeMessage getWrappedMessage() {
        return refCount.getWrapped();
    }


    /**
     * @see org.apache.avalon.framework.activity.Disposable#dispose()
     */
    public synchronized void dispose() {
        if (refCount != null) {
            refCount.decrementReferenceCount();
            refCount = null;
        }
{'fraction_non_alphanumeric': 0.09914281093320394, 'fraction_numerical': 0.0013747371825974446, 'mean_word_length': 2.1396293475501396, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6990870', 'n_tokens_mistral': 2949, 'n_tokens_neox': 2850, 'n_words': 846}