1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.activemq.console; 18 19 import java.io.File; 20 import java.io.InputStream; 21 import java.io.PrintStream; 22 import java.lang.management.ManagementFactory; 23 import java.lang.reflect.InvocationTargetException; 24 import java.lang.reflect.Method; 25 import java.net.JarURLConnection; 26 import java.net.MalformedURLException; 27 import java.net.URI; 28 import java.net.URL; 29 import java.net.URLClassLoader; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.Comparator; 33 import java.util.HashSet; 34 import java.util.Iterator; 35 import java.util.LinkedList; 36 import java.util.List; 37 import java.util.Set; 38 import java.util.StringTokenizer; 39 40 /** 41 * Main class that can bootstrap an ActiveMQ broker console. Handles command 42 * line argument parsing to set up and run broker tasks. 43 * 44 * @version $Revision$ 45 */ 46 public class Main { 47 48 public static final String TASK_DEFAULT_CLASS = "org.apache.activemq.console.command.ShellCommand"; 49 private static boolean useDefExt = true; 50 51 private File activeMQHome; 52 private File activeMQBase; 53 private ClassLoader classLoader; 54 private Set<File> extensions = new HashSet<File>(5); 55 private Set<File> activeMQClassPath = new HashSet<File>(5); 56 57 public static void main(String[] args) { 58 Main app = new Main(); 59 60 // Convert arguments to collection for easier management 61 List<String> tokens = new LinkedList<String>(Arrays.asList(args)); 62 // Parse for extension directory option 63 app.parseExtensions(tokens); 64 65 // lets add the conf directory first, to find the log4j.properties just in case its not 66 // in the activemq.classpath system property or some jar incorrectly includes one 67 File confDir = new File(app.getActiveMQBase(), "conf"); 68 app.addClassPath(confDir); 69 70 // Add the following to the classpath: 71 // 72 // ${activemq.base}/conf 73 // ${activemq.base}/lib/* (only if activemq.base != activemq.home) 74 // ${activemq.home}/lib/* 75 // ${activemq.base}/lib/optional/* (only if activemq.base != 76 // activemq.home) 77 // ${activemq.home}/lib/optional/* 78 // ${activemq.base}/lib/web/* (only if activemq.base != activemq.home) 79 // ${activemq.home}/lib/web/* 80 // 81 if (useDefExt && app.canUseExtdir()) { 82 83 boolean baseIsHome = app.getActiveMQBase().equals(app.getActiveMQHome()); 84 85 File baseLibDir = new File(app.getActiveMQBase(), "lib"); 86 File homeLibDir = new File(app.getActiveMQHome(), "lib"); 87 88 if (!baseIsHome) { 89 app.addExtensionDirectory(baseLibDir); 90 } 91 app.addExtensionDirectory(homeLibDir); 92 93 if (!baseIsHome) { 94 app.addExtensionDirectory(new File(baseLibDir, "optional")); 95 app.addExtensionDirectory(new File(baseLibDir, "web")); 96 } 97 app.addExtensionDirectory(new File(homeLibDir, "optional")); 98 app.addExtensionDirectory(new File(homeLibDir, "web")); 99 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.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].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 }