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    }