1. /*
  2. * Copyright 2003-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.ant.loader;
  18. import java.io.File;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.InputStreamReader;
  22. import java.io.Reader;
  23. import org.apache.tools.ant.AntClassLoader;
  24. import org.apache.tools.ant.Project;
  25. import java.util.jar.Manifest;
  26. import java.util.jar.JarFile;
  27. import java.util.zip.ZipFile;
  28. import java.util.jar.Attributes;
  29. import java.util.jar.Attributes.Name;
  30. import java.net.URL;
  31. import java.net.MalformedURLException;
  32. import java.util.zip.ZipEntry;
  33. import java.util.Collections;
  34. import java.util.HashMap;
  35. import java.util.Map;
  36. import java.util.StringTokenizer;
  37. import org.apache.tools.ant.util.FileUtils;
  38. /**
  39. * An implementation of the AntClassLoader suitable for use on post JDK 1.1
  40. * platforms
  41. *
  42. */
  43. public class AntClassLoader2 extends AntClassLoader {
  44. /** Instance of a utility class to use for file operations. */
  45. private FileUtils fileUtils;
  46. /** Static map of jar file/time to manifiest class-path entries */
  47. private static Map pathMap = Collections.synchronizedMap(new HashMap());
  48. /**
  49. * Constructor
  50. */
  51. public AntClassLoader2() {
  52. fileUtils = FileUtils.newFileUtils();
  53. }
  54. /**
  55. * Define a class given its bytes
  56. *
  57. * @param container the container from which the class data has been read
  58. * may be a directory or a jar/zip file.
  59. *
  60. * @param classData the bytecode data for the class
  61. * @param className the name of the class
  62. *
  63. * @return the Class instance created from the given data
  64. *
  65. * @throws IOException if the class data cannot be read.
  66. */
  67. protected Class defineClassFromData(File container, byte[] classData,
  68. String className) throws IOException {
  69. definePackage(container, className);
  70. return defineClass(className, classData, 0, classData.length,
  71. Project.class.getProtectionDomain());
  72. }
  73. /**
  74. * Get the manifest from the given jar, if it is indeed a jar and it has a
  75. * manifest
  76. *
  77. * @param container the File from which a manifest is required.
  78. *
  79. * @return the jar's manifest or null is the container is not a jar or it
  80. * has no manifest.
  81. *
  82. * @exception IOException if the manifest cannot be read.
  83. */
  84. private Manifest getJarManifest(File container) throws IOException {
  85. if (container.isDirectory()) {
  86. return null;
  87. }
  88. JarFile jarFile = null;
  89. try {
  90. jarFile = new JarFile(container);
  91. return jarFile.getManifest();
  92. } finally {
  93. if (jarFile != null) {
  94. jarFile.close();
  95. }
  96. }
  97. }
  98. /**
  99. * Define the package information associated with a class.
  100. *
  101. * @param container the file containing the class definition.
  102. * @param className the class name of for which the package information
  103. * is to be determined.
  104. *
  105. * @exception IOException if the package information cannot be read from the
  106. * container.
  107. */
  108. protected void definePackage(File container, String className)
  109. throws IOException {
  110. int classIndex = className.lastIndexOf('.');
  111. if (classIndex == -1) {
  112. return;
  113. }
  114. String packageName = className.substring(0, classIndex);
  115. if (getPackage(packageName) != null) {
  116. // already defined
  117. return;
  118. }
  119. // define the package now
  120. Manifest manifest = getJarManifest(container);
  121. if (manifest == null) {
  122. definePackage(packageName, null, null, null, null, null,
  123. null, null);
  124. } else {
  125. definePackage(container, packageName, manifest);
  126. }
  127. }
  128. /**
  129. * Define the package information when the class comes from a
  130. * jar with a manifest
  131. *
  132. * @param container the jar file containing the manifest
  133. * @param packageName the name of the package being defined.
  134. * @param manifest the jar's manifest
  135. */
  136. protected void definePackage(File container, String packageName,
  137. Manifest manifest) {
  138. String sectionName = packageName.replace('.', '/') + "/";
  139. String specificationTitle = null;
  140. String specificationVendor = null;
  141. String specificationVersion = null;
  142. String implementationTitle = null;
  143. String implementationVendor = null;
  144. String implementationVersion = null;
  145. String sealedString = null;
  146. URL sealBase = null;
  147. Attributes sectionAttributes = manifest.getAttributes(sectionName);
  148. if (sectionAttributes != null) {
  149. specificationTitle
  150. = sectionAttributes.getValue(Name.SPECIFICATION_TITLE);
  151. specificationVendor
  152. = sectionAttributes.getValue(Name.SPECIFICATION_VENDOR);
  153. specificationVersion
  154. = sectionAttributes.getValue(Name.SPECIFICATION_VERSION);
  155. implementationTitle
  156. = sectionAttributes.getValue(Name.IMPLEMENTATION_TITLE);
  157. implementationVendor
  158. = sectionAttributes.getValue(Name.IMPLEMENTATION_VENDOR);
  159. implementationVersion
  160. = sectionAttributes.getValue(Name.IMPLEMENTATION_VERSION);
  161. sealedString
  162. = sectionAttributes.getValue(Name.SEALED);
  163. }
  164. Attributes mainAttributes = manifest.getMainAttributes();
  165. if (mainAttributes != null) {
  166. if (specificationTitle == null) {
  167. specificationTitle
  168. = mainAttributes.getValue(Name.SPECIFICATION_TITLE);
  169. }
  170. if (specificationVendor == null) {
  171. specificationVendor
  172. = mainAttributes.getValue(Name.SPECIFICATION_VENDOR);
  173. }
  174. if (specificationVersion == null) {
  175. specificationVersion
  176. = mainAttributes.getValue(Name.SPECIFICATION_VERSION);
  177. }
  178. if (implementationTitle == null) {
  179. implementationTitle
  180. = mainAttributes.getValue(Name.IMPLEMENTATION_TITLE);
  181. }
  182. if (implementationVendor == null) {
  183. implementationVendor
  184. = mainAttributes.getValue(Name.IMPLEMENTATION_VENDOR);
  185. }
  186. if (implementationVersion == null) {
  187. implementationVersion
  188. = mainAttributes.getValue(Name.IMPLEMENTATION_VERSION);
  189. }
  190. if (sealedString == null) {
  191. sealedString
  192. = mainAttributes.getValue(Name.SEALED);
  193. }
  194. }
  195. if (sealedString != null && sealedString.equalsIgnoreCase("true")) {
  196. try {
  197. sealBase = new URL("file:" + container.getPath());
  198. } catch (MalformedURLException e) {
  199. // ignore
  200. }
  201. }
  202. definePackage(packageName, specificationTitle, specificationVersion,
  203. specificationVendor, implementationTitle,
  204. implementationVersion, implementationVendor, sealBase);
  205. }
  206. /**
  207. * Add a file to the path. This classloader reads the manifest, if
  208. * available, and adds any additional class path jars specified in the
  209. * manifest.
  210. *
  211. * @param pathComponent the file which is to be added to the path for
  212. * this class loader
  213. *
  214. * @throws IOException if data needed from the file cannot be read.
  215. */
  216. protected void addPathFile(File pathComponent) throws IOException {
  217. super.addPathFile(pathComponent);
  218. if (pathComponent.isDirectory()) {
  219. return;
  220. }
  221. String absPathPlusTimeAndLength =
  222. pathComponent.getAbsolutePath() + pathComponent.lastModified() + "-"
  223. + pathComponent.length();
  224. String classpath = (String) pathMap.get(absPathPlusTimeAndLength);
  225. if (classpath == null) {
  226. ZipFile jarFile = null;
  227. InputStream manifestStream = null;
  228. try {
  229. jarFile = new ZipFile(pathComponent);
  230. manifestStream
  231. = jarFile.getInputStream(new ZipEntry("META-INF/MANIFEST.MF"));
  232. if (manifestStream == null) {
  233. return;
  234. }
  235. Reader manifestReader
  236. = new InputStreamReader(manifestStream, "UTF-8");
  237. org.apache.tools.ant.taskdefs.Manifest manifest
  238. = new org.apache.tools.ant.taskdefs.Manifest(manifestReader);
  239. classpath
  240. = manifest.getMainSection().getAttributeValue("Class-Path");
  241. } catch (org.apache.tools.ant.taskdefs.ManifestException e) {
  242. // ignore
  243. } finally {
  244. if (manifestStream != null) {
  245. manifestStream.close();
  246. }
  247. if (jarFile != null) {
  248. jarFile.close();
  249. }
  250. }
  251. if (classpath == null) {
  252. classpath = "";
  253. }
  254. pathMap.put(absPathPlusTimeAndLength, classpath);
  255. }
  256. if (!"".equals(classpath)) {
  257. URL baseURL = fileUtils.getFileURL(pathComponent);
  258. StringTokenizer st = new StringTokenizer(classpath);
  259. while (st.hasMoreTokens()) {
  260. String classpathElement = st.nextToken();
  261. URL libraryURL = new URL(baseURL, classpathElement);
  262. if (!libraryURL.getProtocol().equals("file")) {
  263. log("Skipping jar library " + classpathElement
  264. + " since only relative URLs are supported by this"
  265. + " loader", Project.MSG_VERBOSE);
  266. continue;
  267. }
  268. File libraryFile = new File(libraryURL.getFile());
  269. if (libraryFile.exists() && !isInPath(libraryFile)) {
  270. addPathFile(libraryFile);
  271. }
  272. }
  273. }
  274. }
  275. }