alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 *
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.javacore;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import org.netbeans.jmi.javamodel.Element;
import org.netbeans.mdr.util.TransactionMutex;
import org.netbeans.modules.javacore.api.JavaModel;
import org.netbeans.modules.javacore.jmiimpl.javamodel.ResourceImpl;
import org.netbeans.modules.javacore.jmiimpl.javamodel.SemiPersistentElement;
import org.openide.ErrorManager;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.netbeans.modules.javacore.jmiimpl.javamodel.BehavioralFeatureImpl;
import org.netbeans.modules.javacore.jmiimpl.javamodel.EnumConstantImpl;
import org.netbeans.modules.javacore.jmiimpl.javamodel.FeatureImpl;
import org.netbeans.modules.javacore.jmiimpl.javamodel.FieldImpl;
import org.netbeans.modules.javacore.internalapi.ExternalChange;
import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
import org.netbeans.modules.javacore.classpath.FilterClassPathImplementation;
import org.netbeans.api.java.classpath.ClassPath;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObject;
import javax.swing.SwingUtilities;

/**
 *
 * @author Martin Matula
 */
public class ExclusiveMutex extends TransactionMutex {
    private static final boolean DEBUG = false;
    
    private boolean changes = false;
    private boolean fail = false;
    private Thread thread = null;
    private int counter = 0;

    private Set newObjects = new HashSet();

    // set of changed resources
    private Set changedRscSet = new HashSet();
    private Set changedExternalSet = new HashSet();
    
    private Set upToDateElements = new HashSet();
    private Set invalidClasses = new HashSet();

    // list of BehavioralElements, which has to be reinitialized after commit
    private List initBodyQueue;
    
    // cache used by MDRParser for scopes
    private Map parserCache;

    private Set needParsing = new HashSet();
    private Set needParsingRW = new HashSet();
    private Set needTSUpdate = new HashSet();
    private Set priorityThreads = null;

    private ClassPath classPath = null;
    private volatile boolean isSwingWaiting = false;
    private JMManager manager = (JMManager) JMManager.getManager();

    /** Creates a new instance of ExclusiveMutex */
    public ExclusiveMutex(Object p1, Object p2, Object p3) {
        super(p1, p2, p3);
    }

    // can be called only from within a transaction
    public boolean isSwingWaiting() {
        return isSwingWaiting;
    }
    
    public synchronized void addPriorityThread() {
        if (priorityThreads == null) {
            priorityThreads = new HashSet();
        }
        priorityThreads.add(Thread.currentThread());
    }
    
    public synchronized void enter(boolean writeAccess) {
        Thread thread = Thread.currentThread();
        if (SwingUtilities.isEventDispatchThread()) addPriorityThread();
        boolean isPriorityThread = priorityThreads != null && priorityThreads.remove(thread);
        while (!(this.thread == thread || (counter == 0 && (!isSwingWaiting || isPriorityThread)))) {
            try {
                isSwingWaiting |= isPriorityThread;
//                if (isPriorityThread) {
//                    System.err.println("Priority thread waiting: " + thread.toString());
//                    Thread.dumpStack();
//                }
                this.wait();
            } catch (InterruptedException e) {
                ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
            }
        }
//            if (writeAccess && endTransInProgress) {
//                throw new IllegalStateException("Cannot do modifications while commit/rollback in progress.");
//            }
        this.thread = thread;
        counter++;
        
        if (JMManager.TRANS_DEBUG && counter == 1) {
            Thread.dumpStack(); 
        }

        if (isPriorityThread) {
//            System.out.print(".");
            if (priorityThreads != null && priorityThreads.isEmpty()) {
                priorityThreads = null;
            }
            isSwingWaiting = priorityThreads != null;
        }
        
        if (writeAccess) {
            if (!changes) {
                changes = true;
                start();
            }
        }
        boolean success = false;
        try {
            parseIfNeeded(writeAccess && counter == 1);
            success = true;
        } finally {
            if (!success) {
                try {
                    if (changes && counter == 1) {
                        end(true);
                        notifyElements();
                        ClassIndex.rollback();
                    }
                } catch (RuntimeException x) {
                    // throw the original exception rather than x
                    ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, x);
                } finally {
                    counter--;
                    if (counter == 0) {
                        thread = null;
                        this.fail = false;
                        changes = false;
                        this.notifyAll();
                    }
                }
            }
        }
        if (counter == 1) clearClassPath();
    }

    private void clearClassPath() {
        classPath = null;
    }
    
    private void clearParserCache() {
        parserCache=null;
    }

    private void parseIfNeeded(boolean writeAccess) {
        boolean isEmpty;
        DataObject[] jdos;
        
        synchronized (needParsingRW) {
            isEmpty = needParsingRW.isEmpty();
        }
        if (!isEmpty) {
            synchronized (needParsingRW) {
                jdos = (DataObject[]) needParsingRW.toArray(new DataObject[needParsingRW.size()]);
                needParsingRW.clear();
            }

            for (int i = 0; i < jdos.length; i++) {
                DataObject dobj=jdos[i];

                if (!dobj.isValid()) continue;
                JavaModel.setClassPath(dobj.getPrimaryFile());
                RepositoryUpdater.createOrUpdateResource(dobj);
            }
        }

        if (writeAccess) {
            synchronized (needParsing) {
                isEmpty = needParsing.isEmpty();
            }
        
            if (!isEmpty) {
                synchronized (needParsing) {
                    jdos = (DataObject[]) needParsing.toArray(new DataObject[needParsing.size()]);
                    if (DEBUG) {
                        System.err.println("cleaning needParsing"); // NOI18N
                        Thread.dumpStack();
                    }
                    needParsing.clear();
                }
                for (int i = 0; i < jdos.length; i++) {
                    DataObject dobj=jdos[i];
                    FileObject fobj;
                    ResourceImpl res;

                    if (!dobj.isValid()) continue;
                    fobj=dobj.getPrimaryFile();
                    JavaModel.setClassPath(fobj);
                    res = (ResourceImpl) JavaMetamodel.getManager().getResource(fobj);
                    if (res != null) {
                        res.updateFromDataObject(dobj,true);
                    }
                }
            }
        }
        
        synchronized (needTSUpdate) {
            isEmpty = needTSUpdate.isEmpty();
        }
        if (!isEmpty) {
            FileObject[] fos;
            synchronized (needTSUpdate) {
                fos = (FileObject[]) needTSUpdate.toArray(new FileObject[needTSUpdate.size()]);
                needTSUpdate.clear();
            }

            for (int i = 0; i < fos.length; i++) {
                FileObject fo = fos[i];
                if (!fo.isValid()) continue;
                RepositoryUpdater.updateTimeStamp(fo);
            }
        }
    }
    
    public Thread getThread() {
        return thread;
    }
    
    public void addModified(DataObject jdo) {
        if (DEBUG) {
            System.out.println("adding " + jdo + " to needParsing"); // NOI18N
            Thread.dumpStack();
        }
        needParsing.add(jdo); 
    }
     
    public boolean removeModified(DataObject jdo) {
        if (DEBUG) {
            System.out.println("removing " + jdo + " from needParsing"); // NOI18N
            Thread.dumpStack();
        }
        return needParsing.remove(jdo);
    }
    
    void addModifiedRW(DataObject jdo) {
        synchronized (needParsingRW) {
            synchronized (needParsing) {
                needParsingRW.add(jdo);
                needParsing.remove(jdo);
            }
        }
    }
    
    void addUpdateTS(FileObject fo) {
        synchronized (needTSUpdate) {
            needTSUpdate.add(fo);
        }
    }
    
    public synchronized boolean leave(boolean fail) {
        assert thread == Thread.currentThread() : "Cannot end transaction from a different thread!";
        if (fail) {
            JMManager.getLog().notify(ErrorManager.INFORMATIONAL,new Exception("rollback!!!")); // NOI18N
        }
        boolean result = false;
        try {
            if (changes) {
                this.fail |= fail;
            } else {
                if (fail) throw new RuntimeException("Cannot fail in read mode."); // NOI18N
            }

            if (counter == 1) {
                result = true;
                if (changes) {
//                    endTransInProgress = true;
                    if (this.fail) {
                        end(true);
                        notifyElements();
                        ClassIndex.rollback();
                    } else {
                        notifyElements();
                        end(false);
                        ClassIndex.commit();
                    }
                }
            }
        } catch (RuntimeException x) {
            ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, x);
            this.fail = true;
            try {
                end(true);
            } catch (RuntimeException e) {
                ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
            }
            try {
                notifyElements();
            } catch (RuntimeException e) {
                ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
            }
            try {
                ClassIndex.rollback();
            } catch (RuntimeException e) {
                ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
            }
        } finally {
                if (counter > 0) {
                    if ((--counter) == 0) {
                        try {
//                            endTransInProgress = false;
                            thread = null;
                            this.fail = false;
                            if (changes) {
                                //needParsing.clear();
                                changes = false;
                            }
                            clearClassPath();
                            clearParserCache();
                        } finally {
                            this.notifyAll();
                        }
                    }
                } else {
                    throw new RuntimeException("Error: leave() without enter()."); // NOI18N
                }
        }
        return result;
    }

    void setClassPath(List/**/ cp, boolean preferSources) {
        classPath = FilterClassPathImplementation.createClassPath(cp, preferSources);
    }
    
    public void setClassPath(ClassPath cp) {
        classPath = cp;
    }

    public ClassPath getClassPath() {
        return classPath;
    }
    
    public Map getParserCache() {
        if (parserCache==null)
            parserCache=new HashMap();
        return parserCache;
    }
    
    public void invalidateAtCommit(Element element) {
        invalidClasses.add(element);
    }

    public boolean pendingChanges() {
        return changes;
    }

    public boolean willFail() {
        return fail;
    }

    public void registerChange(ResourceImpl resource) {
        changedRscSet.add(resource);
    }
    
    public void registerPersisted(SemiPersistentElement element) {
        upToDateElements.add(element);
    }

    public void unregisterChange(ResourceImpl resource) {
        changedRscSet.remove(resource);
    }

    public void registerExtChange(ExternalChange change) {
        changedExternalSet.add(change);
    }

    public void addNew(Element h) {
        newObjects.add(h);
    }

    public void removeNew(Element h) {
        newObjects.remove(h);
    }

    public void addBFeatureToInitQueue(FeatureImpl bf) {
        initBodyQueue.add(bf);
    }

    private void notifyElements() {
        if (!upToDateElements.isEmpty()) {
            for (Iterator it = upToDateElements.iterator(); it.hasNext();) {
                ((SemiPersistentElement) it.next()).clearPersist(this.fail);
            }
            if (!this.fail) {
                upToDateElements.clear();
            }
        }
        if (!changedRscSet.isEmpty() || !changedExternalSet.isEmpty()) {
            JavaMetamodel.getUndoManager().transactionStarted();
        }
        initBodyQueue = new ArrayList();
        for (Iterator resIt = changedRscSet.iterator(); resIt.hasNext(); ) {
            ResourceImpl resource = (ResourceImpl) resIt.next();
            if (this.fail) {
                resource.rollbackChanges();
            } else {
                resource.commitChanges();
                if (!newObjects.isEmpty()) {
                    throw new RuntimeException("Some objects were not added to a resource or deleted before commit."); // NOI18N
                }
            }
        }
        
        if (!this.fail) {
            for (Iterator it = invalidClasses.iterator(); it.hasNext();) {
                ((Element) it.next()).refDelete();
                it.remove();
            }
            ResourceImpl resource;
            Iterator resIt;            
            for (resIt = changedRscSet.iterator(); resIt.hasNext(); ) {
                resource = (ResourceImpl) resIt.next();                
                resource.parseResource();
            }
            for (resIt = changedRscSet.iterator(); resIt.hasNext(); ) {
                resource = (ResourceImpl) resIt.next();                
                resource.commitConfirmed();
                DataObject jdo = ((JMManager) JavaMetamodel.getManager()).getDataObject(resource);
                if (DEBUG) System.out.println("removing " + jdo + " from needParsing"); // NOI18N
                synchronized (needParsing) {
                    needParsing.remove(jdo);
                }
                synchronized (needParsing) {
                    needParsingRW.remove(jdo);
                }
            }
        }
        for (Iterator bfIt = initBodyQueue.iterator(); bfIt.hasNext(); ) {
            FeatureImpl bf=(FeatureImpl)bfIt.next();
            if (bf instanceof BehavioralFeatureImpl) {
                ((BehavioralFeatureImpl)bf).initBody();
            } else if (bf instanceof FieldImpl) {
                ((FieldImpl)bf).initInitValue();
            } else {
                ((EnumConstantImpl) bf).initInitValue();
            }
        }
        
        initBodyQueue=null;        
        for (Iterator it = changedExternalSet.iterator(); it.hasNext(); ) {
            ExternalChange change = (ExternalChange) it.next();
            if (this.fail) {
                change.undoExternalChange();
            } else {
                JavaMetamodel.getUndoManager().addItem(change);
                change.performExternalChange();
            }
        }
        
        if (!changedRscSet.isEmpty() || !changedExternalSet.isEmpty()) {
            JavaMetamodel.getUndoManager().transactionEnded(this.fail);
        }

        changedExternalSet.clear();
        changedRscSet.clear();
        newObjects.clear();
    }
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.