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