001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.geronimo.system.main;
018    
019    import java.io.PrintStream;
020    
021    import org.apache.geronimo.kernel.Kernel;
022    import org.apache.geronimo.kernel.repository.Artifact;
023    import org.apache.geronimo.system.serverinfo.ServerConstants;
024    
025    /**
026     * A startup monitor that shows the progress of loading and starting
027     * modules using a text based progress bar and the use of line
028     * feeds to update the progress display, therefore minimizing the
029     * number of lines output to the terminal.
030     * <p/>
031     * This implementation displays a fixed-size bar.
032     * <p/> 
033     * A summary will also be produced containing a list of ports
034     * Geronimo is listening on, the configIds of application modules
035     * that were started and the context roots of Web applications that were started.
036     *
037     * @version $Revision: 1.0$
038     */
039    public class SimpleProgressBarStartupMonitor implements StartupMonitor {
040        private final static char STATUS_NOT_READY = ' ';
041        private final static char STATUS_LOADING = '-';
042        private final static char STATUS_LOADED = '>';
043        private final static char STATUS_STARTED = '*';
044        private final static char STATUS_FAILED = 'x';
045        private final static int MAX_WIDTH = 79;
046        private PrintStream out;
047        private String currentOperation;
048        private Artifact[] modules;
049        private char[] moduleStatus = new char[0];
050        private long started;
051        private int percent = 0;
052        private Kernel kernel;
053        private int operationLimit = 50;
054        private boolean finished = false;
055        private UpdateThread thread;
056            
057        private int barSize = 40;
058        private int barProgress;
059    
060        public synchronized void systemStarting(long startTime) {
061            out = System.out;
062            started = startTime;
063        }
064    
065        public synchronized void systemStarted(Kernel kernel) {
066            out.println("Starting Geronimo Application Server v" + ServerConstants.getVersion());
067            this.kernel = kernel;
068            currentOperation = "Loading";
069        }
070    
071        public synchronized void foundModules(Artifact[] modules) {
072            this.modules = modules;
073            moduleStatus = new char[modules.length];
074            for (int i = 0; i < moduleStatus.length; i++) {
075                moduleStatus[i] = STATUS_NOT_READY;
076            }
077            operationLimit = MAX_WIDTH
078                    - 3 // two brackets, space afterward
079                    - barSize // module tokens
080                    - 5 // 3 digits of percent plus % plus space afterward
081                    - 5;// 3 digits of time plus s plus space afterward
082            repaint();
083            thread = new UpdateThread();
084            thread.start();
085        }
086    
087        public synchronized void calculatePercent() {
088            if (finished) {
089                this.percent = 100;
090                this.barProgress = this.barSize;
091                return;
092            }
093            int percent = 0;
094            if (kernel != null) percent += 5;
095            int total = moduleStatus.length * 2;
096            int progress = 0;
097            for (int i = 0; i < moduleStatus.length; i++) {
098                char c = moduleStatus[i];
099                switch (c) {
100                    case STATUS_LOADED:
101                        progress += 1;
102                        break;
103                    case STATUS_STARTED:
104                    case STATUS_FAILED:
105                        progress += 2;
106                        break;
107                }
108            }
109            percent += Math.round(95f * (float) progress / (float) total);
110            this.percent = percent;
111            this.barProgress =  (this.barSize * this.percent) / 100;
112        }
113    
114        public synchronized void moduleLoading(Artifact module) {
115            currentOperation = " Loading " + module;
116            for (int i = 0; i < modules.length; i++) {
117                if (modules[i].equals(module)) {
118                    moduleStatus[i] = STATUS_LOADING;
119                    break;
120                }
121            }
122            repaint();
123        }
124    
125        public synchronized void moduleLoaded(Artifact module) {
126            for (int i = 0; i < modules.length; i++) {
127                if (modules[i].equals(module)) {
128                    moduleStatus[i] = STATUS_LOADED;
129                    break;
130                }
131            }
132            calculatePercent();
133            repaint();
134        }
135    
136        public synchronized void moduleStarting(Artifact module) {
137            currentOperation = "Starting " + module;
138        }
139    
140        public synchronized void moduleStarted(Artifact module) {
141            for (int i = 0; i < modules.length; i++) {
142                if (modules[i].equals(module)) {
143                    moduleStatus[i] = STATUS_STARTED;
144                    break;
145                }
146            }
147            calculatePercent();
148            repaint();
149        }
150    
151        public synchronized void startupFinished() {
152            finished = true;
153            currentOperation = "Startup complete";
154            calculatePercent();
155            thread.done = true;
156            thread.interrupt();
157        }
158    
159        public synchronized void serverStartFailed(Exception problem) {
160            currentOperation = "Startup failed";
161            repaint();
162            out.println();
163            problem.printStackTrace(out);
164        }
165    
166        private synchronized void repaint() {
167            StringBuffer buf = new StringBuffer();
168            buf.append("\r[");        
169            for (int i = 0; i < barSize; i++) {
170                if (i < barProgress) {
171                    buf.append("*");
172                } else {
173                    buf.append(" ");
174                }
175            }        
176            buf.append("] ");
177            if (percent < 10) {
178                buf.append("  ");
179            } else if (percent >= 10 && percent < 100) {
180                buf.append(" ");
181            }
182            buf.append(percent).append("% ");
183            int time = Math.round((float) (System.currentTimeMillis() - started) / 1000f);
184            if (time < 10) {
185                buf.append(' ');
186            }
187            if (time < 100) {
188                buf.append(' ');
189            }
190            buf.append(time).append("s ");
191            if (currentOperation.length() > operationLimit) {
192                if (operationLimit > 3) {
193                    buf.append(currentOperation.substring(0, operationLimit - 3)).append("...");
194                } else {
195                    for (int i = 0; i < operationLimit; i++) {
196                        buf.append('.');
197                    }
198                }
199            } else {
200                buf.append(currentOperation);
201                for (int i = currentOperation.length(); i < operationLimit; i++) {
202                    buf.append(' ');
203                }
204            }
205            out.print(buf.toString());
206            out.flush();
207        }
208    
209        private class UpdateThread extends Thread {
210            private volatile boolean done = false;
211    
212            public UpdateThread() {
213                super("Progress Display Update Thread");
214                setDaemon(true);
215            }
216    
217            public void run() {
218                while (!done) {
219                    try {
220                        Thread.sleep(500);
221                    } catch (InterruptedException e) {
222                        continue;
223                    }
224                    repaint();
225                }
226    
227                repaint();
228                out.println();
229                StartupMonitorUtil.wrapUp(SimpleProgressBarStartupMonitor.this.out, SimpleProgressBarStartupMonitor.this.kernel);
230            }
231        }
232    }