You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Jar.java 14 kB

Addition of ZipFileset facilities. Descibed by the author --- With these patches, Zip (and derivative tasks such as Jar and War) can merge the entries of multiple zip files into a single output zip file. The contents of an input zip file may be selectively extracted based on include/exclude patterns. An included zip file is specified using a <fileset> with a "src" attribute, as in: <target name="jartest"> <jar jarfile="utils.jar"> <fileset src="weblogic.jar" includes="weblogic/utils/" excludes="weblogic/utils/jars/,**/reflect/" /> </jar> </target> In this example, a subset of the "weblogic/utils" directory is extracted from weblogic.jar, into utils.jar. The fileset may also contain "prefix" and "fullpath" attributes (the functionality of PrefixedFileSet has been retained in the new class ZipFileSet). Prefixes apply to directory-based and zip-based filesets. The fullpath attributes applies only to a single file in a directory-based fileset. The War task may extract entries from a zip file for all of its filesets (including the files in "classes" and "lib"). The motivation for this change is: 1) There is significant overlap between "jlink" and "zip", and it seemed better to combine them. 2) "jlink" does not support include/exclude patterns which are extremely useful for writing packaging-type tasks such as Zip/Jar/War. This was my main motivation. 3) By adding this functionality to the base task, it can also be used in derivative tasks such as Jar and War. --- Submitted By: Don Ferguson <don@bea.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268458 13f79535-47bb-0310-9956-ffa450edef68
25 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2000-2002 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Ant", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.tools.ant.taskdefs;
  55. import org.apache.tools.ant.BuildException;
  56. import org.apache.tools.ant.FileScanner;
  57. import org.apache.tools.ant.Project;
  58. import org.apache.tools.ant.types.ZipFileSet;
  59. import org.apache.tools.zip.ZipOutputStream;
  60. import java.io.IOException;
  61. import java.io.File;
  62. import java.io.InputStream;
  63. import java.io.Reader;
  64. import java.io.FileReader;
  65. import java.io.ByteArrayOutputStream;
  66. import java.io.PrintWriter;
  67. import java.io.ByteArrayInputStream;
  68. import java.io.OutputStreamWriter;
  69. import java.io.InputStreamReader;
  70. import java.util.Enumeration;
  71. /**
  72. * Creates a JAR archive.
  73. *
  74. * @author James Davidson <a href="mailto:duncan@x180.com">duncan@x180.com</a>
  75. */
  76. public class Jar extends Zip {
  77. /** The index file name. */
  78. private final static String INDEX_NAME = "META-INF/INDEX.LIST";
  79. private File manifestFile;
  80. private Manifest manifest;
  81. private Manifest execManifest;
  82. /** true if a manifest has been specified in the task */
  83. private boolean buildFileManifest = false;
  84. /** jar index is JDK 1.3+ only */
  85. private boolean index = false;
  86. /** constructor */
  87. public Jar() {
  88. super();
  89. archiveType = "jar";
  90. emptyBehavior = "create";
  91. setEncoding("UTF8");
  92. }
  93. public void setWhenempty(WhenEmpty we) {
  94. log("JARs are never empty, they contain at least a manifest file",
  95. Project.MSG_WARN);
  96. }
  97. /**
  98. * @deprecated Use setDestFile(File) instead
  99. */
  100. public void setJarfile(File jarFile) {
  101. log("DEPRECATED - The jarfile attribute is deprecated. Use destfile attribute instead.");
  102. setDestFile(jarFile);
  103. }
  104. /**
  105. * Set whether or not to create an index list for classes
  106. * to speed up classloading.
  107. */
  108. public void setIndex(boolean flag){
  109. index = flag;
  110. }
  111. public void addConfiguredManifest(Manifest newManifest) throws ManifestException {
  112. if (manifest == null) {
  113. manifest = Manifest.getDefaultManifest();
  114. }
  115. manifest.merge(newManifest);
  116. buildFileManifest = true;
  117. }
  118. public void setManifest(File manifestFile) {
  119. if (!manifestFile.exists()) {
  120. throw new BuildException("Manifest file: " + manifestFile + " does not exist.",
  121. getLocation());
  122. }
  123. this.manifestFile = manifestFile;
  124. Reader r = null;
  125. try {
  126. r = new FileReader(manifestFile);
  127. Manifest newManifest = new Manifest(r);
  128. if (manifest == null) {
  129. manifest = Manifest.getDefaultManifest();
  130. }
  131. manifest.merge(newManifest);
  132. }
  133. catch (ManifestException e) {
  134. log("Manifest is invalid: " + e.getMessage(), Project.MSG_ERR);
  135. throw new BuildException("Invalid Manifest: " + manifestFile, e, getLocation());
  136. }
  137. catch (IOException e) {
  138. throw new BuildException("Unable to read manifest file: " + manifestFile, e);
  139. }
  140. finally {
  141. if (r != null) {
  142. try {
  143. r.close();
  144. }
  145. catch (IOException e) {
  146. // do nothing
  147. }
  148. }
  149. }
  150. }
  151. public void addMetainf(ZipFileSet fs) {
  152. // We just set the prefix for this fileset, and pass it up.
  153. fs.setPrefix("META-INF/");
  154. super.addFileset(fs);
  155. }
  156. protected void initZipOutputStream(ZipOutputStream zOut)
  157. throws IOException, BuildException
  158. {
  159. try {
  160. execManifest = Manifest.getDefaultManifest();
  161. if (manifest != null) {
  162. execManifest.merge(manifest);
  163. }
  164. for (Enumeration e = execManifest.getWarnings(); e.hasMoreElements(); ) {
  165. log("Manifest warning: " + (String)e.nextElement(), Project.MSG_WARN);
  166. }
  167. zipDir(null, zOut, "META-INF/");
  168. // time to write the manifest
  169. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  170. PrintWriter writer = new PrintWriter(baos);
  171. execManifest.write(writer);
  172. writer.flush();
  173. ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
  174. super.zipFile(bais, zOut, "META-INF/MANIFEST.MF", System.currentTimeMillis());
  175. super.initZipOutputStream(zOut);
  176. }
  177. catch (ManifestException e) {
  178. log("Manifest is invalid: " + e.getMessage(), Project.MSG_ERR);
  179. throw new BuildException("Invalid Manifest", e, getLocation());
  180. }
  181. }
  182. protected void finalizeZipOutputStream(ZipOutputStream zOut)
  183. throws IOException, BuildException {
  184. if (index) {
  185. createIndexList(zOut);
  186. }
  187. }
  188. /**
  189. * Create the index list to speed up classloading.
  190. * This is a JDK 1.3+ specific feature and is enabled by default.
  191. * {@link http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#JAR%20Index}
  192. * @param zOut the zip stream representing the jar being built.
  193. * @throws IOException thrown if there is an error while creating the
  194. * index and adding it to the zip stream.
  195. */
  196. private void createIndexList(ZipOutputStream zOut) throws IOException {
  197. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  198. // encoding must be UTF8 as specified in the specs.
  199. PrintWriter writer = new PrintWriter(new OutputStreamWriter(baos, "UTF8"));
  200. // version-info blankline
  201. writer.println("JarIndex-Version: 1.0");
  202. writer.println();
  203. // header newline
  204. writer.println(zipFile.getName());
  205. // JarIndex is sorting the directories by ascending order.
  206. // it's painful to do in JDK 1.1 and it has no value but cosmetic
  207. // since it will be read into a hashtable by the classloader.
  208. Enumeration enum = addedDirs.keys();
  209. while (enum.hasMoreElements()) {
  210. String dir = (String)enum.nextElement();
  211. // try to be smart, not to be fooled by a weird directory name
  212. // @fixme do we need to check for directories starting by ./ ?
  213. dir = dir.replace('\\', '/');
  214. int pos = dir.lastIndexOf('/');
  215. if (pos != -1){
  216. dir = dir.substring(0, pos);
  217. }
  218. // looks like nothing from META-INF should be added
  219. // and the check is not case insensitive.
  220. // see sun.misc.JarIndex
  221. if ( dir.startsWith("META-INF") ){
  222. continue;
  223. }
  224. // name newline
  225. writer.println(dir);
  226. }
  227. writer.flush();
  228. ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
  229. super.zipFile(bais, zOut, INDEX_NAME, System.currentTimeMillis());
  230. }
  231. /**
  232. * Handle situation when we encounter a manifest file
  233. *
  234. * If we haven't been given one, we use this one.
  235. *
  236. * If we have, we merge the manifest in, provided it is a new file
  237. * and not the old one from the JAR we are updating
  238. */
  239. private void zipManifestEntry(InputStream is) throws IOException {
  240. try {
  241. if (execManifest == null) {
  242. execManifest = new Manifest(new InputStreamReader(is));
  243. }
  244. else if (isAddingNewFiles()) {
  245. execManifest.merge(new Manifest(new InputStreamReader(is)));
  246. }
  247. }
  248. catch (ManifestException e) {
  249. log("Manifest is invalid: " + e.getMessage(), Project.MSG_ERR);
  250. throw new BuildException("Invalid Manifest", e, getLocation());
  251. }
  252. }
  253. protected void zipFile(File file, ZipOutputStream zOut, String vPath)
  254. throws IOException
  255. {
  256. // If the file being added is META-INF/MANIFEST.MF, we warn if it's not the
  257. // one specified in the "manifest" attribute - or if it's being added twice,
  258. // meaning the same file is specified by the "manifeset" attribute and in
  259. // a <fileset> element.
  260. if (vPath.equalsIgnoreCase("META-INF/MANIFEST.MF")) {
  261. log("Warning: selected "+archiveType+" files include a META-INF/MANIFEST.MF which will be ignored " +
  262. "(please use manifest attribute to "+archiveType+" task)", Project.MSG_WARN);
  263. } else {
  264. super.zipFile(file, zOut, vPath);
  265. }
  266. }
  267. protected void zipFile(InputStream is, ZipOutputStream zOut, String vPath, long lastModified)
  268. throws IOException
  269. {
  270. // If the file being added is META-INF/MANIFEST.MF, we merge it with the
  271. // current manifest
  272. if (vPath.equalsIgnoreCase("META-INF/MANIFEST.MF")) {
  273. try {
  274. zipManifestEntry(is);
  275. }
  276. catch (IOException e) {
  277. throw new BuildException("Unable to read manifest file: ", e);
  278. }
  279. } else {
  280. super.zipFile(is, zOut, vPath, lastModified);
  281. }
  282. }
  283. /**
  284. * Check whether the archive is up-to-date;
  285. * @param scanners list of prepared scanners containing files to archive
  286. * @param zipFile intended archive file (may or may not exist)
  287. * @return true if nothing need be done (may have done something already); false if
  288. * archive creation should proceed
  289. * @exception BuildException if it likes
  290. */
  291. protected boolean isUpToDate(FileScanner[] scanners, File zipFile) throws BuildException {
  292. // need to handle manifest as a special check
  293. if (buildFileManifest || manifestFile == null) {
  294. java.util.zip.ZipFile theZipFile = null;
  295. try {
  296. theZipFile = new java.util.zip.ZipFile(zipFile);
  297. java.util.zip.ZipEntry entry = theZipFile.getEntry("META-INF/MANIFEST.MF");
  298. if (entry == null) {
  299. log("Updating jar since the current jar has no manifest", Project.MSG_VERBOSE);
  300. return false;
  301. }
  302. Manifest currentManifest = new Manifest(new InputStreamReader(theZipFile.getInputStream(entry)));
  303. if (manifest == null) {
  304. manifest = Manifest.getDefaultManifest();
  305. }
  306. if (!currentManifest.equals(manifest)) {
  307. log("Updating jar since jar manifest has changed", Project.MSG_VERBOSE);
  308. return false;
  309. }
  310. }
  311. catch (Exception e) {
  312. // any problems and we will rebuild
  313. log("Updating jar since cannot read current jar manifest: " + e.getClass().getName() + e.getMessage(),
  314. Project.MSG_VERBOSE);
  315. return false;
  316. }
  317. finally {
  318. if (theZipFile != null) {
  319. try {
  320. theZipFile.close();
  321. }
  322. catch (IOException e) {
  323. //ignore
  324. }
  325. }
  326. }
  327. }
  328. else if (manifestFile.lastModified() > zipFile.lastModified()) {
  329. return false;
  330. }
  331. return super.isUpToDate(scanners, zipFile);
  332. }
  333. protected boolean createEmptyZip(File zipFile) {
  334. // Jar files always contain a manifest and can never be empty
  335. return true;
  336. }
  337. /**
  338. * Make sure we don't think we already have a MANIFEST next time this task
  339. * gets executed.
  340. */
  341. protected void cleanUp() {
  342. super.cleanUp();
  343. }
  344. }