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: */package org.apache.openejb.config;
017:
018: import static org.apache.openejb.util.URLs.toFile;
019:
020: import static java.net.URLDecoder.decode;
021:
022: import org.apache.openejb.config.sys.Deployments;
023: import org.apache.openejb.config.sys.JaxbOpenejb;
024: import org.apache.openejb.loader.FileUtils;
025: import org.apache.openejb.loader.SystemInstance;
026: import org.apache.openejb.util.Logger;
027: import org.apache.xbean.finder.UrlSet;
028:
029: import java.io.File;
030: import java.io.IOException;
031: import java.net.URL;
032: import java.net.URLDecoder;
033: import java.util.ArrayList;
034: import java.util.Collections;
035: import java.util.HashMap;
036: import java.util.List;
037:
038: /**
039: * @version $Rev: 634140 $ $Date: 2008-03-05 19:20:40 -0800 $
040: */
041: public class DeploymentsResolver {
042:
043: private static final String CLASSPATH_INCLUDE = "openejb.deployments.classpath.include";
044: private static final String CLASSPATH_EXCLUDE = "openejb.deployments.classpath.exclude";
045: private static final String CLASSPATH_REQUIRE_DESCRIPTOR = "openejb.deployments.classpath.require.descriptor";
046: private static final String CLASSPATH_FILTER_DESCRIPTORS = "openejb.deployments.classpath.filter.descriptors";
047: private static final String CLASSPATH_FILTER_SYSTEMAPPS = "openejb.deployments.classpath.filter.systemapps";
048: private static final Logger logger = DeploymentLoader.logger;
049:
050: private static void loadFrom(Deployments dep, FileUtils path,
051: List<String> jarList) {
052:
053: ////////////////////////////////
054: //
055: // Expand the path of a jar
056: //
057: ////////////////////////////////
058: if (dep.getDir() == null && dep.getJar() != null) {
059: try {
060: File jar = path.getFile(dep.getJar(), false);
061: if (!jarList.contains(jar.getAbsolutePath())) {
062: jarList.add(jar.getAbsolutePath());
063: }
064: } catch (Exception ignored) {
065: }
066: return;
067: }
068:
069: File dir = null;
070: try {
071: dir = path.getFile(dep.getDir(), false);
072: } catch (Exception ignored) {
073: }
074:
075: if (dir == null || !dir.isDirectory())
076: return;
077:
078: ////////////////////////////////
079: //
080: // Unpacked "Jar" directory with descriptor
081: //
082: ////////////////////////////////
083: File ejbJarXml = new File(dir, "META-INF" + File.separator
084: + "ejb-jar.xml");
085: if (ejbJarXml.exists()) {
086: if (!jarList.contains(dir.getAbsolutePath())) {
087: jarList.add(dir.getAbsolutePath());
088: }
089: return;
090: }
091:
092: File appXml = new File(dir, "META-INF" + File.separator
093: + "application.xml");
094: if (appXml.exists()) {
095: if (!jarList.contains(dir.getAbsolutePath())) {
096: jarList.add(dir.getAbsolutePath());
097: }
098: return;
099: }
100:
101: ////////////////////////////////
102: //
103: // Directory contains Jar files
104: //
105: ////////////////////////////////
106: boolean hasNestedArchives = false;
107: for (File file : dir.listFiles()) {
108: if (file.getName().endsWith(".jar")
109: || file.getName().endsWith(".war")
110: || file.getName().endsWith(".rar")
111: || file.getName().endsWith(".ear")) {
112: if (jarList.contains(file.getAbsolutePath()))
113: continue;
114: jarList.add(file.getAbsolutePath());
115: hasNestedArchives = true;
116: }
117: }
118:
119: ////////////////////////////////
120: //
121: // Unpacked "Jar" directory w/o descriptor
122: //
123: ////////////////////////////////
124: if (!hasNestedArchives) {
125: HashMap<String, URL> files = new HashMap<String, URL>();
126: DeploymentLoader.scanDir(dir, files, "");
127: for (String fileName : files.keySet()) {
128: if (fileName.endsWith(".class")) {
129: if (!jarList.contains(dir.getAbsolutePath())) {
130: jarList.add(dir.getAbsolutePath());
131: }
132: return;
133: }
134: }
135: }
136: }
137:
138: public static List<String> resolveAppLocations(
139: List<Deployments> deployments) {
140: // make a copy of the list because we update it
141: deployments = new ArrayList<Deployments>(deployments);
142:
143: //// getOption ///////////////////////////////// BEGIN ////////////////////
144: String flag = SystemInstance.get().getProperty(
145: "openejb.deployments.classpath", "true").toLowerCase();
146: boolean searchClassPath = flag.equals("true");
147: //// getOption ///////////////////////////////// END ////////////////////
148:
149: if (searchClassPath) {
150: Deployments deployment = JaxbOpenejb.createDeployments();
151: deployment.setClasspath(Thread.currentThread()
152: .getContextClassLoader());
153: deployments.add(deployment);
154: }
155: // resolve jar locations ////////////////////////////////////// BEGIN ///////
156:
157: FileUtils base = SystemInstance.get().getBase();
158:
159: List<String> jarList = new ArrayList<String>(deployments.size());
160: try {
161: for (Deployments deployment : deployments) {
162: if (deployment.getClasspath() != null) {
163: loadFromClasspath(base, jarList, deployment
164: .getClasspath());
165: } else {
166: loadFrom(deployment, base, jarList);
167: }
168: }
169: } catch (SecurityException ignored) {
170: }
171:
172: return jarList;
173: }
174:
175: /**
176: * The algorithm of OpenEJB deployments class-path inclusion and exclusion is implemented as follows:
177: * 1- If the string value of the resource URL matches the include class-path pattern
178: * Then load this resource
179: * 2- If the string value of the resource URL matches the exclude class-path pattern
180: * Then ignore this resource
181: * 3- If the include and exclude class-path patterns are not defined
182: * Then load this resource
183: * <p/>
184: * The previous steps are based on the following points:
185: * 1- Include class-path pattern has the highst priority
186: * This helps in case both patterns are defined using the same values.
187: * This appears in step 1 and 2 of the above algorithm.
188: * 2- Loading the resource is the default behaviour in case of not defining a value for any class-path pattern
189: * This appears in step 3 of the above algorithm.
190: */
191: private static void loadFromClasspath(FileUtils base,
192: List<String> jarList, ClassLoader classLoader) {
193:
194: String include = SystemInstance.get().getProperty(
195: CLASSPATH_INCLUDE, "");
196: String exclude = SystemInstance.get().getProperty(
197: CLASSPATH_EXCLUDE, ".*");
198: boolean requireDescriptors = SystemInstance.get().getProperty(
199: CLASSPATH_REQUIRE_DESCRIPTOR, "false")
200: .equalsIgnoreCase("true");
201: boolean filterDescriptors = SystemInstance.get().getProperty(
202: CLASSPATH_FILTER_DESCRIPTORS, "false")
203: .equalsIgnoreCase("true");
204: boolean filterSystemApps = SystemInstance.get().getProperty(
205: CLASSPATH_FILTER_SYSTEMAPPS, "true").equalsIgnoreCase(
206: "true");
207:
208: logger.debug("Using " + CLASSPATH_INCLUDE + " '" + include
209: + "'");
210: logger.debug("Using " + CLASSPATH_EXCLUDE + " '" + exclude
211: + "'");
212: logger.debug("Using " + CLASSPATH_FILTER_SYSTEMAPPS + " '"
213: + filterSystemApps + "'");
214: logger.debug("Using " + CLASSPATH_FILTER_DESCRIPTORS + " '"
215: + filterDescriptors + "'");
216: logger.debug("Using " + CLASSPATH_REQUIRE_DESCRIPTOR + " '"
217: + requireDescriptors + "'");
218:
219: try {
220: UrlSet urlSet = new UrlSet(classLoader);
221: UrlSet includes = urlSet.matching(include);
222: urlSet = urlSet.exclude(ClassLoader.getSystemClassLoader()
223: .getParent());
224: urlSet = urlSet.excludeJavaExtDirs();
225: urlSet = urlSet.excludeJavaEndorsedDirs();
226: urlSet = urlSet.excludeJavaHome();
227: urlSet = urlSet.excludePaths(System.getProperty(
228: "sun.boot.class.path", ""));
229: urlSet = urlSet.exclude(".*/JavaVM.framework/.*");
230: urlSet = urlSet
231: .exclude(".*/activeio-core-[\\d.]+(-incubator)?.jar(!/)?");
232: urlSet = urlSet
233: .exclude(".*/activemq-(core|ra)-[\\d.]+.jar(!/)?");
234: urlSet = urlSet
235: .exclude(".*/annotations-api-6.[01].[\\d.]+.jar(!/)?");
236: urlSet = urlSet
237: .exclude(".*/avalon-framework-[\\d.]+.jar(!/)?");
238: urlSet = urlSet
239: .exclude(".*/axis2-jaxws-api-[\\d.]+.jar(!/)?");
240: urlSet = urlSet
241: .exclude(".*/backport-util-concurrent-[\\d.]+.jar(!/)?");
242: urlSet = urlSet.exclude(".*/catalina-[\\d.]+.jar(!/)?");
243: urlSet = urlSet
244: .exclude(".*/commons-(logging|cli|pool|lang|collections|dbcp)-[\\d.]+.jar(!/)?");
245: urlSet = urlSet.exclude(".*/derby-[\\d.]+.jar(!/)?");
246: urlSet = urlSet
247: .exclude(".*/geronimo-(connector|transaction)-[\\d.]+.jar(!/)?");
248: urlSet = urlSet
249: .exclude(".*/geronimo-[^/]+_spec-[\\d.]+.jar(!/)?");
250: urlSet = urlSet
251: .exclude(".*/geronimo-javamail_([\\d.]+)_mail-[\\d.]+.jar(!/)?");
252: urlSet = urlSet.exclude(".*/hsqldb-[\\d.]+.jar(!/)?");
253: urlSet = urlSet.exclude(".*/idb-[\\d.]+.jar(!/)?");
254: urlSet = urlSet.exclude(".*/idea_rt.jar(!/)?");
255: urlSet = urlSet
256: .exclude(".*/jaxb-(impl|api)-[\\d.]+.jar(!/)?");
257: urlSet = urlSet
258: .exclude(".*/jmdns-[\\d.]+(-RC\\d)?.jar(!/)?");
259: urlSet = urlSet.exclude(".*/juli-[\\d.]+.jar(!/)?");
260: urlSet = urlSet.exclude(".*/junit-[\\d.]+.jar(!/)?");
261: urlSet = urlSet.exclude(".*/log4j-[\\d.]+.jar(!/)?");
262: urlSet = urlSet.exclude(".*/logkit-[\\d.]+.jar(!/)?");
263: urlSet = urlSet
264: .exclude(".*/openjpa-(jdbc|kernel|lib|persistence|persistence-jdbc)(-5)?-[\\d.]+.jar(!/)?");
265: urlSet = urlSet.exclude(".*/serp-[\\d.]+.jar(!/)?");
266: urlSet = urlSet.exclude(".*/servlet-api-[\\d.]+.jar(!/)?");
267: urlSet = urlSet
268: .exclude(".*/swizzle-stream-[\\d.]+.jar(!/)?");
269: urlSet = urlSet.exclude(".*/wsdl4j-[\\d.]+.jar(!/)?");
270: urlSet = urlSet
271: .exclude(".*/xbean-(reflect|naming|finder)-[\\d.]+.jar(!/)?");
272: urlSet = urlSet
273: .exclude(".*/xmlParserAPIs-[\\d.]+.jar(!/)?");
274: urlSet = urlSet.exclude(".*/xmlunit-[\\d.]+.jar(!/)?");
275: UrlSet prefiltered = urlSet;
276: urlSet = urlSet.exclude(exclude);
277: urlSet = urlSet.include(includes);
278:
279: if (filterSystemApps) {
280: urlSet = urlSet
281: .exclude(".*/openejb-[^/]+(.(jar|ear|war)(!/)?|/target/classes/?)");
282: }
283:
284: List<URL> urls = urlSet.getUrls();
285: int size = urls.size();
286: if (size == 0 && include.length() > 0) {
287: logger
288: .warning("No classpath URLs matched. Current settings: "
289: + CLASSPATH_EXCLUDE
290: + "='"
291: + exclude
292: + "', "
293: + CLASSPATH_INCLUDE
294: + "='"
295: + include + "'");
296: return;
297: } else if (size == 0
298: && (!filterDescriptors && prefiltered.getUrls()
299: .size() == 0)) {
300: return;
301: } else if (size < 10) {
302: logger.debug("Inspecting classpath for applications: "
303: + urls.size() + " urls.");
304: } else if (size < 50 && !requireDescriptors) {
305: logger
306: .info("Inspecting classpath for applications: "
307: + urls.size()
308: + " urls. Consider adjusting your exclude/include. Current settings: "
309: + CLASSPATH_EXCLUDE + "='" + exclude
310: + "', " + CLASSPATH_INCLUDE + "='"
311: + include + "'");
312: } else if (!requireDescriptors) {
313: logger
314: .warning("Inspecting classpath for applications: "
315: + urls.size() + " urls.");
316: logger
317: .warning("ADJUST THE EXCLUDE/INCLUDE!!!. Current settings: "
318: + CLASSPATH_EXCLUDE
319: + "='"
320: + exclude
321: + "', "
322: + CLASSPATH_INCLUDE
323: + "='"
324: + include + "'");
325: }
326:
327: long begin = System.currentTimeMillis();
328: processUrls(urls, classLoader, !requireDescriptors, base,
329: jarList);
330: long end = System.currentTimeMillis();
331: long time = end - begin;
332:
333: UrlSet unchecked = new UrlSet();
334: if (!filterDescriptors) {
335: unchecked = prefiltered.exclude(urlSet);
336: if (filterSystemApps) {
337: unchecked = unchecked
338: .exclude(".*/openejb-[^/]+(.(jar|ear|war)(./)?|/target/classes/?)");
339: }
340: processUrls(unchecked.getUrls(), classLoader, false,
341: base, jarList);
342: }
343:
344: if (logger.isDebugEnabled()) {
345: logger.debug("URLs after filtering: "
346: + urlSet.getUrls().size()
347: + unchecked.getUrls().size());
348: for (URL url : urlSet.getUrls()) {
349: logger.debug("Annotations path: " + url);
350: }
351: for (URL url : unchecked.getUrls()) {
352: logger.debug("Descriptors path: " + url);
353: }
354: }
355:
356: if (urls.size() == 0)
357: return;
358:
359: if (time < 1000) {
360: logger.debug("Searched " + urls.size()
361: + " classpath urls in " + time
362: + " milliseconds. Average "
363: + (time / urls.size())
364: + " milliseconds per url.");
365: } else if (time < 4000 || urls.size() < 3) {
366: logger.info("Searched " + urls.size()
367: + " classpath urls in " + time
368: + " milliseconds. Average "
369: + (time / urls.size())
370: + " milliseconds per url.");
371: } else if (time < 10000) {
372: logger.warning("Searched " + urls.size()
373: + " classpath urls in " + time
374: + " milliseconds. Average "
375: + (time / urls.size())
376: + " milliseconds per url.");
377: logger.warning("Consider adjusting your "
378: + CLASSPATH_EXCLUDE + " and "
379: + CLASSPATH_INCLUDE
380: + " settings. Current settings: exclude='"
381: + exclude + "', include='" + include + "'");
382: } else {
383: logger.fatal("Searched " + urls.size()
384: + " classpath urls in " + time
385: + " milliseconds. Average "
386: + (time / urls.size())
387: + " milliseconds per url. TOO LONG!");
388: logger
389: .fatal("ADJUST THE EXCLUDE/INCLUDE!!!. Current settings: "
390: + CLASSPATH_EXCLUDE
391: + "='"
392: + exclude
393: + "', "
394: + CLASSPATH_INCLUDE
395: + "='"
396: + include + "'");
397: List<String> list = new ArrayList<String>();
398: for (URL url : urls) {
399: list.add(url.toExternalForm());
400: }
401: Collections.sort(list);
402: for (String url : list) {
403: logger.info("Matched: " + url);
404: }
405: }
406: } catch (IOException e1) {
407: e1.printStackTrace();
408: logger.warning(
409: "Unable to search classpath for modules: Received Exception: "
410: + e1.getClass().getName() + " "
411: + e1.getMessage(), e1);
412: }
413:
414: }
415:
416: private static void processUrls(List<URL> urls,
417: ClassLoader classLoader,
418: boolean searchForDescriptorlessApplications,
419: FileUtils base, List<String> jarList) {
420: Deployments deployment;
421: String path;
422: for (URL url : urls) {
423: try {
424: Class moduleType = DeploymentLoader.discoverModuleType(
425: url, classLoader,
426: searchForDescriptorlessApplications);
427: if (AppModule.class.isAssignableFrom(moduleType)
428: || EjbModule.class.isAssignableFrom(moduleType)) {
429: deployment = JaxbOpenejb.createDeployments();
430: if (url.getProtocol().equals("jar")) {
431: url = new URL(url.getFile().replaceFirst(
432: "!.*$", ""));
433: File file = toFile(url);
434: path = file.getAbsolutePath();
435: deployment.setJar(path);
436: } else if (url.getProtocol().equals("file")) {
437: File file = toFile(url);
438: path = file.getAbsolutePath();
439: deployment.setDir(path);
440: } else {
441: logger.warning("Not loading "
442: + moduleType.getSimpleName()
443: + ". Unknown protocol "
444: + url.getProtocol());
445: continue;
446: }
447: logger.info("Found " + moduleType.getSimpleName()
448: + " in classpath: " + path);
449: loadFrom(deployment, base, jarList);
450: }
451: } catch (IOException e) {
452: logger.warning(
453: "Unable to determine the module type of "
454: + url.toExternalForm()
455: + ": Exception: " + e.getMessage(), e);
456: } catch (UnknownModuleTypeException ignore) {
457: }
458: }
459: }
460: }
|