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 }