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 }