001    package org.apache.geronimo.system.main;
002    
003    import java.io.PrintStream;
004    
005    import org.apache.geronimo.kernel.Kernel;
006    import org.apache.geronimo.kernel.repository.Artifact;
007    import org.apache.geronimo.system.serverinfo.ServerConstants;
008    
009    /**
010     * A startup monitor that shows the progress of loading and starting
011     * modules using a text based progress bar and the use of line
012     * feeds to update the progress display, therefore minimizing the
013     * number of lines output to the terminal.
014     * <p/>
015     * A summary will also be produced containing a list of ports
016     * Geronimo is listening on, the configIds of application modules
017     * that were started and the URLs of Web applications that were started.
018     *
019     * @version $Revision: 1.0$
020     */
021    public class ProgressBarStartupMonitor implements StartupMonitor {
022        private final static char STATUS_NOT_READY = ' ';
023        private final static char STATUS_LOADING = '-';
024        private final static char STATUS_LOADED = '>';
025        private final static char STATUS_STARTED = '*';
026        private final static char STATUS_FAILED = 'x';
027        private final static int MAX_WIDTH = 70;
028        private PrintStream out;
029        private String currentOperation;
030        private Artifact[] modules;
031        private char[] moduleStatus = new char[0];
032        private long started;
033        private int percent = 0;
034        private Kernel kernel;
035        private int operationLimit = 50;
036        private boolean finished = false;
037        private UpdateThread thread;
038    
039        public synchronized void systemStarting(long startTime) {
040            out = System.out;
041            started = startTime;
042        }
043    
044        public synchronized void systemStarted(Kernel kernel) {
045            out.println("Starting Geronimo Application Server v" + ServerConstants.getVersion());
046            this.kernel = kernel;
047            currentOperation = "Loading";
048        }
049    
050        public synchronized void foundModules(Artifact[] modules) {
051            this.modules = modules;
052            moduleStatus = new char[modules.length];
053            for (int i = 0; i < moduleStatus.length; i++) {
054                moduleStatus[i] = STATUS_NOT_READY;
055            }
056            operationLimit = MAX_WIDTH
057                    - 5 // two brackets, start and stop tokens, space afterward
058                    - modules.length // module tokens
059                    - 4 // 2 digits of percent plus % plus space afterward
060                    - 5;// 3 digits of time plus s plus space afterward
061            repaint();
062            thread = new UpdateThread();
063            thread.start();
064        }
065    
066        public synchronized void calculatePercent() {
067            if (finished) {
068                this.percent = 100;
069                return;
070            }
071            int percent = 0;
072            if (kernel != null) percent += 5;
073            int total = moduleStatus.length * 2;
074            int progress = 0;
075            for (int i = 0; i < moduleStatus.length; i++) {
076                char c = moduleStatus[i];
077                switch (c) {
078                    case STATUS_LOADED:
079                        progress += 1;
080                        break;
081                    case STATUS_STARTED:
082                    case STATUS_FAILED:
083                        progress += 2;
084                        break;
085                }
086            }
087            percent += Math.round(90f * (float) progress / (float) total);
088            this.percent = percent;
089        }
090    
091        public synchronized void moduleLoading(Artifact module) {
092            currentOperation = " Loading " + module;
093            for (int i = 0; i < modules.length; i++) {
094                if (modules[i].equals(module)) {
095                    moduleStatus[i] = STATUS_LOADING;
096                }
097            }
098            repaint();
099        }
100    
101        public synchronized void moduleLoaded(Artifact module) {
102            for (int i = 0; i < modules.length; i++) {
103                if (modules[i].equals(module)) {
104                    moduleStatus[i] = STATUS_LOADED;
105                }
106            }
107            calculatePercent();
108            repaint();
109        }
110    
111        public synchronized void moduleStarting(Artifact module) {
112            currentOperation = "Starting " + module;
113        }
114    
115        public synchronized void moduleStarted(Artifact module) {
116            for (int i = 0; i < modules.length; i++) {
117                if (modules[i].equals(module)) {
118                    moduleStatus[i] = STATUS_STARTED;
119                }
120            }
121            calculatePercent();
122            repaint();
123        }
124    
125        public synchronized void startupFinished() {
126            finished = true;
127            currentOperation = "Startup complete";
128            calculatePercent();
129            thread.done = true;
130            thread.interrupt();
131        }
132    
133        public synchronized void serverStartFailed(Exception problem) {
134            currentOperation = "Startup failed";
135            repaint();
136            out.println();
137            problem.printStackTrace(out);
138        }
139    
140        private synchronized void repaint() {
141            StringBuffer buf = new StringBuffer();
142            buf.append("\r[");
143            buf.append(kernel == null ? STATUS_NOT_READY : STATUS_STARTED);
144            for (int i = 0; i < moduleStatus.length; i++) {
145                buf.append(moduleStatus[i]);
146            }
147            buf.append(finished ? STATUS_STARTED : STATUS_NOT_READY);
148            buf.append("] ");
149            if (percent < 10) {
150                buf.append(' ');
151            }
152            buf.append(percent).append("% ");
153            int time = Math.round((float) (System.currentTimeMillis() - started) / 1000f);
154            if (time < 10) {
155                buf.append(' ');
156            }
157            if (time < 100) {
158                buf.append(' ');
159            }
160            buf.append(time).append("s ");
161            if (currentOperation.length() > operationLimit) {
162                int space = currentOperation.indexOf(' ', 5);
163                buf.append(currentOperation.substring(0, space + 1));
164                // "Foo BarBarBar" limit 9 = "Foo ...ar" = 13 - 9 + 3 + 1 + 3
165                // buf.append("...").append(currentOperation.substring(currentOperation.length()-operationLimit+space+4));
166                // "FooBar BarBarBar" limit 12 = "FooBar Ba..." = (7, 12-3)
167                buf.append(currentOperation.substring(space + 1, operationLimit - 3)).append("...");
168            } else {
169                buf.append(currentOperation);
170                for (int i = currentOperation.length(); i < operationLimit; i++) {
171                    buf.append(' ');
172                }
173            }
174            out.print(buf.toString());
175            out.flush();
176        }
177    
178        private class UpdateThread extends Thread {
179            private volatile boolean done = false;
180    
181            public UpdateThread() {
182                super("Progress Display Update Thread");
183                setDaemon(true);
184            }
185    
186            public void run() {
187                while (!done) {
188                    try {
189                        Thread.sleep(500);
190                    } catch (InterruptedException e) {
191                        continue;
192                    }
193                    repaint();
194                }
195    
196                repaint();
197                out.println();
198                StartupMonitorUtil.wrapUp(ProgressBarStartupMonitor.this.out, ProgressBarStartupMonitor.this.kernel);
199            }
200        }
201    }