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.activemq.console;
018
019 import java.io.File;
020 import java.io.InputStream;
021 import java.io.PrintStream;
022 import java.lang.management.ManagementFactory;
023 import java.lang.reflect.InvocationTargetException;
024 import java.lang.reflect.Method;
025 import java.net.JarURLConnection;
026 import java.net.MalformedURLException;
027 import java.net.URI;
028 import java.net.URL;
029 import java.net.URLClassLoader;
030 import java.util.ArrayList;
031 import java.util.Arrays;
032 import java.util.Comparator;
033 import java.util.HashSet;
034 import java.util.Iterator;
035 import java.util.LinkedList;
036 import java.util.List;
037 import java.util.Set;
038 import java.util.StringTokenizer;
039
040 /**
041 * Main class that can bootstrap an ActiveMQ broker console. Handles command
042 * line argument parsing to set up and run broker tasks.
043 *
044 *
045 */
046 public class Main {
047
048 public static final String TASK_DEFAULT_CLASS = "org.apache.activemq.console.command.ShellCommand";
049 private static boolean useDefExt = true;
050
051 private File activeMQHome;
052 private File activeMQBase;
053 private ClassLoader classLoader;
054 private Set<File> extensions = new HashSet<File>(5);
055 private Set<File> activeMQClassPath = new HashSet<File>(5);
056
057 public static void main(String[] args) {
058 Main app = new Main();
059
060 // Convert arguments to collection for easier management
061 List<String> tokens = new LinkedList<String>(Arrays.asList(args));
062 // Parse for extension directory option
063 app.parseExtensions(tokens);
064
065 // lets add the conf directory first, to find the log4j.properties just in case its not
066 // in the activemq.classpath system property or some jar incorrectly includes one
067 File confDir = new File(app.getActiveMQBase(), "conf");
068 app.addClassPath(confDir);
069
070 // Add the following to the classpath:
071 //
072 // ${activemq.base}/conf
073 // ${activemq.base}/lib/* (only if activemq.base != activemq.home)
074 // ${activemq.home}/lib/*
075 // ${activemq.base}/lib/optional/* (only if activemq.base !=
076 // activemq.home)
077 // ${activemq.home}/lib/optional/*
078 // ${activemq.base}/lib/web/* (only if activemq.base != activemq.home)
079 // ${activemq.home}/lib/web/*
080 //
081 if (useDefExt && app.canUseExtdir()) {
082
083 boolean baseIsHome = app.getActiveMQBase().equals(app.getActiveMQHome());
084
085 File baseLibDir = new File(app.getActiveMQBase(), "lib");
086 File homeLibDir = new File(app.getActiveMQHome(), "lib");
087
088 if (!baseIsHome) {
089 app.addExtensionDirectory(baseLibDir);
090 }
091 app.addExtensionDirectory(homeLibDir);
092
093 if (!baseIsHome) {
094 app.addExtensionDirectory(new File(baseLibDir, "optional"));
095 app.addExtensionDirectory(new File(baseLibDir, "web"));
096 }
097 app.addExtensionDirectory(new File(homeLibDir, "optional"));
098 app.addExtensionDirectory(new File(homeLibDir, "web"));
099
100 }
101
102 // Add any custom classpath specified from the system property
103 // activemq.classpath
104 app.addClassPathList(System.getProperty("activemq.classpath"));
105
106 try {
107 app.runTaskClass(tokens);
108 System.exit(0);
109 } catch (ClassNotFoundException e) {
110 System.out.println("Could not load class: " + e.getMessage());
111 try {
112 ClassLoader cl = app.getClassLoader();
113 if (cl != null) {
114 System.out.println("Class loader setup: ");
115 printClassLoaderTree(cl);
116 }
117 } catch (MalformedURLException e1) {
118 }
119 System.exit(1);
120 } catch (Throwable e) {
121 System.out.println("Failed to execute main task. Reason: " + e);
122 System.exit(1);
123 }
124 }
125
126 /**
127 * Print out what's in the classloader tree being used.
128 *
129 * @param cl
130 * @return depth
131 */
132 private static int printClassLoaderTree(ClassLoader cl) {
133 int depth = 0;
134 if (cl.getParent() != null) {
135 depth = printClassLoaderTree(cl.getParent()) + 1;
136 }
137
138 StringBuffer indent = new StringBuffer();
139 for (int i = 0; i < depth; i++) {
140 indent.append(" ");
141 }
142
143 if (cl instanceof URLClassLoader) {
144 URLClassLoader ucl = (URLClassLoader)cl;
145 System.out.println(indent + cl.getClass().getName() + " {");
146 URL[] urls = ucl.getURLs();
147 for (int i = 0; i < urls.length; i++) {
148 System.out.println(indent + " " + urls[i]);
149 }
150 System.out.println(indent + "}");
151 } else {
152 System.out.println(indent + cl.getClass().getName());
153 }
154 return depth;
155 }
156
157 public void parseExtensions(List<String> tokens) {
158 if (tokens.isEmpty()) {
159 return;
160 }
161
162 int count = tokens.size();
163 int i = 0;
164
165 // Parse for all --extdir and --noDefExt options
166 while (i < count) {
167 String token = tokens.get(i);
168 // If token is an extension dir option
169 if (token.equals("--extdir")) {
170 // Process token
171 count--;
172 tokens.remove(i);
173
174 // If no extension directory is specified, or next token is
175 // another option
176 if (i >= count || tokens.get(i).startsWith("-")) {
177 System.out.println("Extension directory not specified.");
178 System.out.println("Ignoring extension directory option.");
179 continue;
180 }
181
182 // Process extension dir token
183 count--;
184 File extDir = new File(tokens.remove(i));
185
186 if (!canUseExtdir()) {
187 System.out.println("Extension directory feature not available due to the system classpath being able to load: " + TASK_DEFAULT_CLASS);
188 System.out.println("Ignoring extension directory option.");
189 continue;
190 }
191
192 if (!extDir.isDirectory()) {
193 System.out.println("Extension directory specified is not valid directory: " + extDir);
194 System.out.println("Ignoring extension directory option.");
195 continue;
196 }
197
198 addExtensionDirectory(extDir);
199 } else if (token.equals("--noDefExt")) { // If token is
200 // --noDefExt option
201 count--;
202 tokens.remove(i);
203 useDefExt = false;
204 } else {
205 i++;
206 }
207 }
208
209 }
210
211 public void runTaskClass(List<String> tokens) throws Throwable {
212
213 StringBuilder buffer = new StringBuilder();
214 buffer.append(System.getProperty("java.vendor"));
215 buffer.append(" ");
216 buffer.append(System.getProperty("java.version"));
217 buffer.append(" ");
218 buffer.append(System.getProperty("java.home"));
219 System.out.println("Java Runtime: " + buffer.toString());
220
221 buffer = new StringBuilder();
222 buffer.append("current=");
223 buffer.append(Runtime.getRuntime().totalMemory()/1024L);
224 buffer.append("k free=");
225 buffer.append(Runtime.getRuntime().freeMemory()/1024L);
226 buffer.append("k max=");
227 buffer.append(Runtime.getRuntime().maxMemory()/1024L);
228 buffer.append("k");
229 System.out.println(" Heap sizes: " + buffer.toString());
230
231 List jvmArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();
232 buffer = new StringBuilder();
233 for (Object arg : jvmArgs) {
234 buffer.append(" ").append(arg);
235 }
236 System.out.println(" JVM args:" + buffer.toString());
237
238 System.out.println("ACTIVEMQ_HOME: " + getActiveMQHome());
239 System.out.println("ACTIVEMQ_BASE: " + getActiveMQBase());
240
241 ClassLoader cl = getClassLoader();
242 Thread.currentThread().setContextClassLoader(cl);
243
244 // Use reflection to run the task.
245 try {
246 String[] args = tokens.toArray(new String[tokens.size()]);
247 Class task = cl.loadClass(TASK_DEFAULT_CLASS);
248 Method runTask = task.getMethod("main", new Class[] {
249 String[].class, InputStream.class, PrintStream.class
250 });
251 runTask.invoke(task.newInstance(), new Object[] {
252 args, System.in, System.out
253 });
254 } catch (InvocationTargetException e) {
255 throw e.getCause();
256 }
257 }
258
259 public void addExtensionDirectory(File directory) {
260 extensions.add(directory);
261 }
262
263 public void addClassPathList(String fileList) {
264 if (fileList != null && fileList.length() > 0) {
265 StringTokenizer tokenizer = new StringTokenizer(fileList, ";");
266 while (tokenizer.hasMoreTokens()) {
267 addClassPath(new File(tokenizer.nextToken()));
268 }
269 }
270 }
271
272 public void addClassPath(File classpath) {
273 activeMQClassPath.add(classpath);
274 }
275
276 /**
277 * The extension directory feature will not work if the broker factory is
278 * already in the classpath since we have to load him from a child
279 * ClassLoader we build for it to work correctly.
280 *
281 * @return true, if extension dir can be used. false otherwise.
282 */
283 public boolean canUseExtdir() {
284 try {
285 Main.class.getClassLoader().loadClass(TASK_DEFAULT_CLASS);
286 return false;
287 } catch (ClassNotFoundException e) {
288 return true;
289 }
290 }
291
292 public ClassLoader getClassLoader() throws MalformedURLException {
293 if (classLoader == null) {
294 // Setup the ClassLoader
295 classLoader = Main.class.getClassLoader();
296 if (!extensions.isEmpty() || !activeMQClassPath.isEmpty()) {
297
298 ArrayList<URL> urls = new ArrayList<URL>();
299
300 for (Iterator<File> iter = activeMQClassPath.iterator(); iter.hasNext();) {
301 File dir = iter.next();
302 // try{ System.out.println("Adding to classpath: " +
303 // dir.getCanonicalPath()); }catch(Exception e){}
304 urls.add(dir.toURI().toURL());
305 }
306
307 for (Iterator<File> iter = extensions.iterator(); iter.hasNext();) {
308 File dir = iter.next();
309 if (dir.isDirectory()) {
310 File[] files = dir.listFiles();
311 if (files != null) {
312
313 // Sort the jars so that classpath built is
314 // consistently
315 // in the same order. Also allows us to use jar
316 // names to control
317 // classpath order.
318 Arrays.sort(files, new Comparator() {
319 public int compare(Object o1, Object o2) {
320 File f1 = (File)o1;
321 File f2 = (File)o2;
322 return f1.getName().compareTo(f2.getName());
323 }
324 });
325
326 for (int j = 0; j < files.length; j++) {
327 if (files[j].getName().endsWith(".zip") || files[j].getName().endsWith(".jar")) {
328 // try{ System.out.println("Adding to
329 // classpath: " +
330 // files[j].getCanonicalPath());
331 // }catch(Exception e){}
332 urls.add(files[j].toURI().toURL());
333 }
334 }
335 }
336 }
337 }
338
339 URL u[] = new URL[urls.size()];
340 urls.toArray(u);
341 classLoader = new URLClassLoader(u, classLoader);
342 }
343 Thread.currentThread().setContextClassLoader(classLoader);
344 }
345 return classLoader;
346 }
347
348 public void setActiveMQHome(File activeMQHome) {
349 this.activeMQHome = activeMQHome;
350 }
351
352 public File getActiveMQHome() {
353 if (activeMQHome == null) {
354 if (System.getProperty("activemq.home") != null) {
355 activeMQHome = new File(System.getProperty("activemq.home"));
356 }
357
358 if (activeMQHome == null) {
359 // guess from the location of the jar
360 URL url = Main.class.getClassLoader().getResource("org/apache/activemq/console/Main.class");
361 if (url != null) {
362 try {
363 JarURLConnection jarConnection = (JarURLConnection)url.openConnection();
364 url = jarConnection.getJarFileURL();
365 URI baseURI = new URI(url.toString()).resolve("..");
366 activeMQHome = new File(baseURI).getCanonicalFile();
367 System.setProperty("activemq.home", activeMQHome.getAbsolutePath());
368 } catch (Exception ignored) {
369 }
370 }
371 }
372
373 if (activeMQHome == null) {
374 activeMQHome = new File("../.");
375 System.setProperty("activemq.home", activeMQHome.getAbsolutePath());
376 }
377 }
378
379 return activeMQHome;
380 }
381
382 public File getActiveMQBase() {
383 if (activeMQBase == null) {
384 if (System.getProperty("activemq.base") != null) {
385 activeMQBase = new File(System.getProperty("activemq.base"));
386 }
387
388 if (activeMQBase == null) {
389 activeMQBase = getActiveMQHome();
390 System.setProperty("activemq.base", activeMQBase.getAbsolutePath());
391 }
392 }
393
394 return activeMQBase;
395 }
396 }