001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.mx.loading;
023:
024: import java.net.URL;
025: import java.io.InputStream;
026: import java.io.IOException;
027: import java.util.zip.ZipInputStream;
028: import java.util.zip.ZipEntry;
029: import java.util.regex.Pattern;
030: import java.util.regex.Matcher;
031: import java.util.ArrayList;
032: import java.util.Arrays;
033:
034: import org.jboss.logging.Logger;
035:
036: /**
037: A simple service that can be used preload all classes in the classpath
038: of the thread context class loader seen in the start method as the thread
039: context class loader. A simple xmbean fragment for deploying the service
040: is:
041:
042: <mbean code="org.jboss.mx.loading.ClassPreloadService"
043: name="jboss.mx:service=ClassPreloadService"
044: xmbean-dd="">
045: <xmbean>
046: <attribute access="read-write"
047: getMethod="getIncludePatterns" setMethod="setIncludePatterns">
048: <description>The patterns for classpath includes</description>
049: <name>IncludePatterns</name>
050: <type>[Ljava.lang.String;</type>
051: </attribute>
052: <attribute access="read-write"
053: getMethod="getExcludePatterns" setMethod="setExcludePatterns">
054: <description>The patterns for classpath excludes</description>
055: <name>ExcludePatterns</name>
056: <type>[Ljava.lang.String;</type>
057: </attribute>
058: <attribute access="read-write"
059: getMethod="isSimpleMatch" setMethod="setSimpleMatch">
060: <description>A flag indicate if String.endsWith matching of includes/excludes should be used</description>
061: <name>SimpleMatch</name>
062: <type>[Ljava.lang.String;</type>
063: </attribute>
064: <operation>
065: <name>start</name>
066: </operation>
067: <attribute>
068: </xmbean>
069: <attribute name="ExcludePatterns">jbossmq.jar</attribute>
070: <attribute name="IncludePatterns"></attribute>
071: <attribute name="SimpleMatch">true</attribute>
072: </mbean>
073:
074: @author Scott.Stark@jboss.org
075: @version $Revision: 57200 $
076: */
077: public class ClassPreloadService {
078: static Logger log = Logger.getLogger(ClassPreloadService.class);
079: /** The RE expressions for classpath elements to include */
080: private String[] includePattern = {};
081: /** The RE expressions for classpath elements to exclude */
082: private String[] excludePattern = {};
083: /** A flag indicate if String.endsWith matching of includes/excludes should be used */
084: private boolean simpleMatch;
085: boolean trace;
086:
087: public String[] getIncludePatterns() {
088: return includePattern;
089: }
090:
091: public void setIncludePatterns(String[] includePattern) {
092: this .includePattern = includePattern;
093: }
094:
095: public String[] getExcludePatterns() {
096: return excludePattern;
097: }
098:
099: public void setExcludePatterns(String[] excludePattern) {
100: this .excludePattern = excludePattern;
101: }
102:
103: public boolean isSimpleMatch() {
104: return simpleMatch;
105: }
106:
107: public void setSimpleMatch(boolean simpleMatch) {
108: this .simpleMatch = simpleMatch;
109: }
110:
111: public URL[] getRawClassPath() {
112: ClassLoader loader = Thread.currentThread()
113: .getContextClassLoader();
114: URL[] fullCP = ClassLoaderUtils.getClassLoaderURLs(loader);
115: return fullCP;
116: }
117:
118: /**
119: Load all classes seen the TCL classpath. This entails a scan of every
120: archive in the TCL classpath URLs for .class entries.
121: */
122: public void start() {
123: trace = log.isTraceEnabled();
124: log.debug("Starting, includes=" + Arrays.asList(includePattern)
125: + ", excludes=" + excludePattern);
126: // Compile the include/exclude patterns
127: Pattern[] includes = compileIncludes();
128: Pattern[] excludes = compileExcludes();
129:
130: ClassLoader loader = Thread.currentThread()
131: .getContextClassLoader();
132: URL[] rawCP = ClassLoaderUtils.getClassLoaderURLs(loader);
133: URL[] cp = filterCP(rawCP, includes, excludes);
134:
135: int loadedClasses = 0;
136: int loadErrors = 0;
137: for (int n = 0; n < cp.length; n++) {
138: URL u = cp[n];
139: try {
140: InputStream is = u.openStream();
141: ZipInputStream zis = new ZipInputStream(is);
142: ZipEntry ze = zis.getNextEntry();
143: while (ze != null) {
144: String name = ze.getName();
145: if (name.endsWith(".class")) {
146: int length = name.length();
147: String cname = name.replace('/', '.')
148: .substring(0, length - 6);
149: try {
150: Class c = loader.loadClass(cname);
151: loadedClasses++;
152: if (trace)
153: log.trace("loaded class: " + cname);
154: } catch (Throwable e) {
155: loadErrors++;
156: if (trace)
157: log.trace("Failed to load class, "
158: + e.getMessage());
159: }
160: }
161: ze = zis.getNextEntry();
162: }
163: zis.close();
164: } catch (IOException ignore) {
165: // Not a jar
166: }
167: }
168: log.info("Loaded " + loadedClasses + " classes, " + loadErrors
169: + " CNFEs");
170: }
171:
172: public Pattern[] compileIncludes() {
173: ArrayList tmp = new ArrayList();
174: int count = this .includePattern != null ? includePattern.length
175: : 0;
176: for (int n = 0; n < count; n++) {
177: String p = includePattern[n];
178: Pattern pat = Pattern.compile(p);
179: tmp.add(pat);
180: }
181: Pattern[] includes = new Pattern[tmp.size()];
182: tmp.toArray(includes);
183: return includes;
184: }
185:
186: public Pattern[] compileExcludes() {
187: ArrayList tmp = new ArrayList();
188: int count = this .excludePattern != null ? excludePattern.length
189: : 0;
190: for (int n = 0; n < count; n++) {
191: String p = excludePattern[n];
192: Pattern pat = Pattern.compile(p);
193: tmp.add(pat);
194: }
195: Pattern[] includes = new Pattern[tmp.size()];
196: tmp.toArray(includes);
197: return includes;
198: }
199:
200: public URL[] filterCP(URL[] rawCP, Pattern[] includes,
201: Pattern[] excludes) {
202: if (trace)
203: log.trace("filterCP, rawCP=" + Arrays.asList(rawCP));
204: ArrayList tmp = new ArrayList();
205: int count = rawCP != null ? rawCP.length : 0;
206: for (int m = 0; m < count; m++) {
207: URL pathURL = rawCP[m];
208: String path = pathURL.toString();
209: boolean excluded = false;
210:
211: // Excludes take priority over includes
212: for (int n = 0; n < excludes.length; n++) {
213: Pattern p = excludes[n];
214: Matcher matcher = p.matcher(path);
215: if (simpleMatch && path.endsWith(p.pattern())) {
216: excluded = true;
217: break;
218: } else if (matcher.matches()) {
219: excluded = true;
220: break;
221: }
222: }
223: if (excluded) {
224: log.debug("Excluded: " + pathURL);
225: continue;
226: }
227:
228: // If there are no explicit includes, accept the non-excluded paths
229: boolean included = includes.length == 0;
230: for (int n = 0; n < includes.length; n++) {
231: Pattern p = includes[n];
232: Matcher matcher = p.matcher(path);
233: if (simpleMatch && path.endsWith(p.pattern()))
234: tmp.add(pathURL);
235: else if (matcher.matches())
236: tmp.add(pathURL);
237: }
238: if (included) {
239: log.debug("Included: " + pathURL);
240: tmp.add(pathURL);
241: }
242: }
243: URL[] cp = new URL[tmp.size()];
244: tmp.toArray(cp);
245: return cp;
246: }
247: }
|