/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.mx.loading; import java.net.URL; import java.io.InputStream; import java.io.IOException; import java.util.zip.ZipInputStream; import java.util.zip.ZipEntry; import java.util.regex.Pattern; import java.util.regex.Matcher; import java.util.ArrayList; import java.util.Arrays; import org.jboss.logging.Logger; /** A simple service that can be used preload all classes in the classpath of the thread context class loader seen in the start method as the thread context class loader. A simple xmbean fragment for deploying the service is: <mbean code="org.jboss.mx.loading.ClassPreloadService" name="jboss.mx:service=ClassPreloadService" xmbean-dd=""> <xmbean> <attribute access="read-write" getMethod="getIncludePatterns" setMethod="setIncludePatterns"> <description>The patterns for classpath includes</description> <name>IncludePatterns</name> <type>[Ljava.lang.String;</type> </attribute> <attribute access="read-write" getMethod="getExcludePatterns" setMethod="setExcludePatterns"> <description>The patterns for classpath excludes</description> <name>ExcludePatterns</name> <type>[Ljava.lang.String;</type> </attribute> <attribute access="read-write" getMethod="isSimpleMatch" setMethod="setSimpleMatch"> <description>A flag indicate if String.endsWith matching of includes/excludes should be used</description> <name>SimpleMatch</name> <type>[Ljava.lang.String;</type> </attribute> <operation> <name>start</name> </operation> <attribute> </xmbean> <attribute name="ExcludePatterns">jbossmq.jar</attribute> <attribute name="IncludePatterns"></attribute> <attribute name="SimpleMatch">true</attribute> </mbean> @author Scott.Stark@jboss.org @version $Revision: 81022 $ */ public class ClassPreloadService { static Logger log = Logger.getLogger(ClassPreloadService.class); /** The RE expressions for classpath elements to include */ private String[] includePattern = {}; /** The RE expressions for classpath elements to exclude */ private String[] excludePattern = {}; /** A flag indicate if String.endsWith matching of includes/excludes should be used */ private boolean simpleMatch; boolean trace; public String[] getIncludePatterns() { return includePattern; } public void setIncludePatterns(String[] includePattern) { this.includePattern = includePattern; } public String[] getExcludePatterns() { return excludePattern; } public void setExcludePatterns(String[] excludePattern) { this.excludePattern = excludePattern; } public boolean isSimpleMatch() { return simpleMatch; } public void setSimpleMatch(boolean simpleMatch) { this.simpleMatch = simpleMatch; } public URL[] getRawClassPath() { ClassLoader loader = Thread.currentThread().getContextClassLoader(); URL[] fullCP = ClassLoaderUtils.getClassLoaderURLs(loader); return fullCP; } /** Load all classes seen the TCL classpath. This entails a scan of every archive in the TCL classpath URLs for .class entries. */ public void start() { trace = log.isTraceEnabled(); log.debug("Starting, includes="+ Arrays.asList(includePattern) +", excludes="+excludePattern); // Compile the include/exclude patterns Pattern[] includes = compileIncludes(); Pattern[] excludes = compileExcludes(); ClassLoader loader = Thread.currentThread().getContextClassLoader(); URL[] rawCP = ClassLoaderUtils.getClassLoaderURLs(loader); URL[] cp = filterCP(rawCP, includes, excludes); int loadedClasses = 0; int loadErrors = 0; for(int n = 0; n < cp.length; n ++) { URL u = cp[n]; try { InputStream is = u.openStream(); ZipInputStream zis = new ZipInputStream(is); ZipEntry ze = zis.getNextEntry(); while (ze != null) { String name = ze.getName(); if (name.endsWith(".class")) { int length = name.length(); String cname = name.replace('/', '.').substring(0, length - 6); try { Class c = loader.loadClass(cname); loadedClasses ++; if (trace) log.trace("loaded class: " + cname); } catch (Throwable e) { loadErrors ++; if( trace ) log.trace("Failed to load class, "+e.getMessage()); } } ze = zis.getNextEntry(); } zis.close(); } catch (IOException ignore) { // Not a jar } } log.info("Loaded "+loadedClasses+" classes, "+loadErrors+" CNFEs"); } public Pattern[] compileIncludes() { ArrayList tmp = new ArrayList(); int count = this.includePattern != null ? includePattern.length : 0; for(int n = 0; n < count; n ++) { String p = includePattern[n]; Pattern pat = Pattern.compile(p); tmp.add(pat); } Pattern[] includes = new Pattern[tmp.size()]; tmp.toArray(includes); return includes; } public Pattern[] compileExcludes() { ArrayList tmp = new ArrayList(); int count = this.excludePattern != null ? excludePattern.length : 0; for(int n = 0; n < count; n ++) { String p = excludePattern[n]; Pattern pat = Pattern.compile(p); tmp.add(pat); } Pattern[] includes = new Pattern[tmp.size()]; tmp.toArray(includes); return includes; } public URL[] filterCP(URL[] rawCP, Pattern[] includes, Pattern[] excludes) { if( trace ) log.trace("filterCP, rawCP="+Arrays.asList(rawCP)); ArrayList tmp = new ArrayList(); int count = rawCP != null ? rawCP.length : 0; for(int m = 0; m < count; m ++) { URL pathURL = rawCP[m]; String path = pathURL.toString(); boolean excluded = false; // Excludes take priority over includes for(int n = 0; n < excludes.length; n ++) { Pattern p = excludes[n]; Matcher matcher = p.matcher(path); if( simpleMatch && path.endsWith(p.pattern()) ) { excluded = true; break; } else if( matcher.matches() ) { excluded = true; break; } } if( excluded ) { log.debug("Excluded: "+pathURL); continue; } // If there are no explicit includes, accept the non-excluded paths boolean included = includes.length == 0; for(int n = 0; n < includes.length; n ++) { Pattern p = includes[n]; Matcher matcher = p.matcher(path); if( simpleMatch && path.endsWith(p.pattern()) ) tmp.add(pathURL); else if( matcher.matches() ) tmp.add(pathURL); } if( included ) { log.debug("Included: "+pathURL); tmp.add(pathURL); } } URL[] cp = new URL[tmp.size()]; tmp.toArray(cp); return cp; } }