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