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     * A summary will also be produced containing a list of ports
032     * Geronimo is listening on, the configIds of application modules
033     * that were started and the URLs of Web applications that were started.
034     *
035     * @version $Revision: 1.0$
036     */
037    public class ProgressBarStartupMonitor implements StartupMonitor {
038        private final static char STATUS_NOT_READY = ' ';
039        private final static char STATUS_LOADING = '-';
040        private final static char STATUS_LOADED = '>';
041        private final static char STATUS_STARTED = '*';
042        private final static char STATUS_FAILED = 'x';
043        private final static int MAX_WIDTH = 79;
044        private PrintStream out;
045        private String currentOperation;
046        private Artifact[] modules;
047        private char[] moduleStatus = new char[0];
048        private long started;
049        private int percent = 0;
050        private Kernel kernel;
051        private int operationLimit = 50;
052        private boolean finished = false;
053        private UpdateThread thread;
054    
055        public synchronized void systemStarting(long startTime) {
056            out = System.out;
057            started = startTime;
058        }
059    
060        public synchronized void systemStarted(Kernel kernel) {
061            out.println("Starting Geronimo Application Server v" + ServerConstants.getVersion());
062            this.kernel = kernel;
063            currentOperation = "Loading";
064        }
065    
066        public synchronized void foundModules(Artifact[] modules) {
067            this.modules = modules;
068            moduleStatus = new char[modules.length];
069            for (int i = 0; i < moduleStatus.length; i++) {
070                moduleStatus[i] = STATUS_NOT_READY;
071            }
072            operationLimit = MAX_WIDTH
073                    - 5 // two brackets, start and stop tokens, space afterward
074                    - modules.length // module tokens
075                    - 4 // 2 digits of percent plus % plus space afterward
076                    - 5;// 3 digits of time plus s plus space afterward
077            repaint();
078            thread = new UpdateThread();
079            thread.start();
080        }
081    
082        public synchronized void calculatePercent() {
083            if (finished) {
084                this.percent = 100;
085                return;
086            }
087            int percent = 0;
088            if (kernel != null) percent += 5;
089            int total = moduleStatus.length * 2;
090            int progress = 0;
091            for (int i = 0; i < moduleStatus.length; i++) {
092                char c = moduleStatus[i];
093                switch (c) {
094                    case STATUS_LOADED:
095                        progress += 1;
096                        break;
097                    case STATUS_STARTED:
098                    case STATUS_FAILED:
099                        progress += 2;
100                        break;
101                }
102            }
103            percent += Math.round(90f * (float) progress / (float) total);
104            this.percent = percent;
105        }
106    
107        public synchronized void moduleLoading(Artifact module) {
108            currentOperation = " Loading " + module;
109            for (int i = 0; i < modules.length; i++) {
110                if (modules[i].equals(module)) {
111                    moduleStatus[i] = STATUS_LOADING;
112                }
113            }
114            repaint();
115        }
116    
117        public synchronized void moduleLoaded(Artifact module) {
118            for (int i = 0; i < modules.length; i++) {
119                if (modules[i].equals(module)) {
120                    moduleStatus[i] = STATUS_LOADED;
121                }
122            }
123            calculatePercent();
124            repaint();
125        }
126    
127        public synchronized void moduleStarting(Artifact module) {
128            currentOperation = "Starting " + module;
129        }
130    
131        public synchronized void moduleStarted(Artifact module) {
132            for (int i = 0; i < modules.length; i++) {
133                if (modules[i].equals(module)) {
134                    moduleStatus[i] = STATUS_STARTED;
135                }
136            }
137            calculatePercent();
138            repaint();
139        }
140    
141        public synchronized void startupFinished() {
142            finished = true;
143            currentOperation = "Startup complete";
144            calculatePercent();
145            thread.done = true;
146            thread.interrupt();
147        }
148    
149        public synchronized void serverStartFailed(Exception problem) {
150            currentOperation = "Startup failed";
151            repaint();
152            out.println();
153            problem.printStackTrace(out);
154        }
155    
156        private synchronized void repaint() {
157            StringBuffer buf = new StringBuffer();
158            buf.append("\r[");
159            buf.append(kernel == null ? STATUS_NOT_READY : STATUS_STARTED);
160            for (int i = 0; i < moduleStatus.length; i++) {
161                buf.append(moduleStatus[i]);
162            }
163            buf.append(finished ? STATUS_STARTED : STATUS_NOT_READY);
164            buf.append("] ");
165            if (percent < 10) {
166                buf.append(' ');
167            }
168            buf.append(percent).append("% ");
169            int time = Math.round((float) (System.currentTimeMillis() - started) / 1000f);
170            if (time < 10) {
171                buf.append(' ');
172            }
173            if (time < 100) {
174                buf.append(' ');
175            }
176            buf.append(time).append("s ");
177            if (currentOperation.length() > operationLimit) {
178                if (operationLimit > 3) {
179                    buf.append(currentOperation.substring(0, operationLimit - 3)).append("...");
180                } else {
181                    for (int i = 0; i < operationLimit; i++) {
182                        buf.append('.');
183                    }
184                }
185                // int space = currentOperation.indexOf(' ', 5);
186                // buf.append(currentOperation.substring(0, space + 1));
187                // "Foo BarBarBar" limit 9 = "Foo ...ar" = 13 - 9 + 3 + 1 + 3
188                // buf.append("...").append(currentOperation.substring(currentOperation.length()-operationLimit+space+4));
189                // "FooBar BarBarBar" limit 12 = "FooBar Ba..." = (7, 12-3)
190                // buf.append(currentOperation.substring(space + 1, operationLimit - 3)).append("...");
191            } else {
192                buf.append(currentOperation);
193                for (int i = currentOperation.length(); i < operationLimit; i++) {
194                    buf.append(' ');
195                }
196            }
197            out.print(buf.toString());
198            out.flush();
199        }
200    
201        private class UpdateThread extends Thread {
202            private volatile boolean done = false;
203    
204            public UpdateThread() {
205                super("Progress Display Update Thread");
206                setDaemon(true);
207            }
208    
209            public void run() {
210                while (!done) {
211                    try {
212                        Thread.sleep(500);
213                    } catch (InterruptedException e) {
214                        continue;
215                    }
216                    repaint();
217                }
218    
219                repaint();
220                out.println();
221                StartupMonitorUtil.wrapUp(ProgressBarStartupMonitor.this.out, ProgressBarStartupMonitor.this.kernel);
222            }
223        }
224    }