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    }