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 }