001    /*
002     * JBoss, Home of Professional Open Source.
003     * Copyright 2008, Red Hat Middleware LLC, and individual contributors
004     * as indicated by the @author tags. See the copyright.txt file in the
005     * distribution for a full listing of individual contributors. 
006     *
007     * This is free software; you can redistribute it and/or modify it
008     * under the terms of the GNU Lesser General Public License as
009     * published by the Free Software Foundation; either version 2.1 of
010     * the License, or (at your option) any later version.
011     *
012     * This software is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015     * Lesser General Public License for more details.
016     *
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this software; if not, write to the Free
019     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021     */
022    
023    package org.jboss.dna.common.monitor;
024    
025    import java.util.Locale;
026    import java.util.concurrent.atomic.AtomicBoolean;
027    import java.util.concurrent.locks.ReadWriteLock;
028    import java.util.concurrent.locks.ReentrantReadWriteLock;
029    import net.jcip.annotations.GuardedBy;
030    import org.jboss.dna.common.collection.Problems;
031    import org.jboss.dna.common.collection.SimpleProblems;
032    import org.jboss.dna.common.i18n.I18n;
033    
034    /**
035     * A basic progress monitor.
036     * <p>
037     * This class is thread-safe except when accessing or adding {@link #getProblems() problems}. Problems must only be added by the
038     * {@link ProgressMonitor <strong>Updater</strong>}, and accessed by {@link ProgressMonitor Observers} only after the activity
039     * has been {@link #done() completed}.
040     * </p>
041     * 
042     * @author Randall Hauch
043     * @author John Verhaeg
044     */
045    public class SimpleProgressMonitor implements ProgressMonitor {
046    
047        @GuardedBy( "lock" )
048        private I18n taskName;
049        @GuardedBy( "lock" )
050        private Object[] taskNameParams;
051        @GuardedBy( "lock" )
052        private double totalWork;
053        @GuardedBy( "lock" )
054        private double worked;
055    
056        private final String activityName;
057        private final ReadWriteLock lock = new ReentrantReadWriteLock();
058        private final AtomicBoolean cancelled = new AtomicBoolean(false);
059        private final Problems problems = new SimpleProblems();
060    
061        public SimpleProgressMonitor( String activityName ) {
062            this.activityName = activityName != null ? activityName.trim() : "";
063            this.taskName = null;
064            this.taskNameParams = null;
065        }
066    
067        /**
068         * {@inheritDoc}
069         */
070        public String getActivityName() {
071            return this.activityName;
072        }
073    
074        /**
075         * {@inheritDoc}
076         */
077        public void beginTask( double totalWork,
078                               I18n name,
079                               Object... params ) {
080            assert totalWork > 0;
081            try {
082                this.lock.writeLock().lock();
083                this.taskName = name;
084                this.taskNameParams = params;
085                this.totalWork = totalWork;
086                this.worked = 0.0d;
087            } finally {
088                this.lock.writeLock().unlock();
089            }
090        }
091    
092        /**
093         * {@inheritDoc}
094         */
095        public ProgressMonitor createSubtask( double subtaskWork ) {
096            return new SubProgressMonitor(this, subtaskWork);
097        }
098    
099        /**
100         * <p>
101         * {@inheritDoc}
102         * </p>
103         * 
104         * @see org.jboss.dna.common.monitor.ProgressMonitor#done()
105         */
106        public void done() {
107            boolean alreadyDone = false;
108            try {
109                this.lock.writeLock().lock();
110                if (this.worked < this.totalWork) {
111                    this.worked = this.totalWork;
112                } else {
113                    alreadyDone = true;
114                }
115            } finally {
116                this.lock.writeLock().unlock();
117            }
118            if (!alreadyDone) notifyProgress();
119        }
120    
121        /**
122         * {@inheritDoc}
123         */
124        public boolean isCancelled() {
125            return this.cancelled.get();
126        }
127    
128        /**
129         * {@inheritDoc}
130         * 
131         * @see org.jboss.dna.common.monitor.ProgressMonitor#isDone()
132         */
133        public boolean isDone() {
134            lock.readLock().lock();
135            try {
136                return worked >= totalWork;
137            } finally {
138                lock.readLock().unlock();
139            }
140        }
141    
142        /**
143         * {@inheritDoc}
144         */
145        public void setCancelled( boolean value ) {
146            this.cancelled.set(value);
147        }
148    
149        /**
150         * {@inheritDoc}
151         */
152        public void worked( double work ) {
153            if (work > 0) {
154                try {
155                    this.lock.writeLock().lock();
156                    if (this.worked < this.totalWork) {
157                        this.worked += work;
158                        if (this.worked > this.totalWork) this.worked = this.totalWork;
159                    }
160                } finally {
161                    this.lock.writeLock().unlock();
162                }
163                notifyProgress();
164            }
165        }
166    
167        /**
168         * {@inheritDoc}
169         */
170        public ProgressStatus getStatus( Locale locale ) {
171            try {
172                this.lock.readLock().lock();
173                String localizedTaskName = this.taskName == null ? "" : this.taskName.text(locale, this.taskNameParams);
174                return new ProgressStatus(this.getActivityName(), localizedTaskName, this.worked, this.totalWork, this.isCancelled());
175            } finally {
176                this.lock.readLock().unlock();
177            }
178        }
179    
180        /**
181         * Method that is called in {@link #worked(double)} (which is called by {@link #createSubtask(double) subtasks}) when there
182         * has been some positive work, or when the monitor is first marked as {@link #done()}.
183         * <p>
184         * This method implementation does nothing, but subclasses can easily override this method if they want to be updated with the
185         * latest progress.
186         * </p>
187         */
188        protected void notifyProgress() {
189            // do nothing
190        }
191    
192        /**
193         * {@inheritDoc}
194         * <p>
195         * Problems must only be added by the {@link ProgressMonitor <strong>Updater</strong>}, and accessed by
196         * {@link ProgressMonitor Observers} only after the activity has been {@link #done() completed}.
197         * </p>
198         * 
199         * @see org.jboss.dna.common.monitor.ProgressMonitor#getProblems()
200         */
201        public Problems getProblems() {
202            return problems;
203        }
204    }