1. /*
  2. * Copyright 2002-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.taskdefs.optional.extension;
  18. import java.io.File;
  19. import java.io.FileOutputStream;
  20. import java.io.IOException;
  21. import java.util.ArrayList;
  22. import java.util.Iterator;
  23. import java.util.jar.Attributes;
  24. import java.util.jar.Manifest;
  25. import org.apache.tools.ant.BuildException;
  26. import org.apache.tools.ant.Project;
  27. import org.apache.tools.ant.Task;
  28. /**
  29. * Generates a manifest that declares all the dependencies.
  30. * The dependencies are determined by looking in the
  31. * specified path and searching for Extension / "Optional Package"
  32. * specifications in the manifests of the jars.
  33. *
  34. * <p>Prior to JDK1.3, an "Optional Package" was known as an Extension.
  35. * The specification for this mechanism is available in the JDK1.3
  36. * documentation in the directory
  37. * $JDK_HOME/docs/guide/extensions/versioning.html. Alternatively it is
  38. * available online at <a href="http://java.sun.com/j2se/1.3/docs/guide/extensions/versioning.html">
  39. * http://java.sun.com/j2se/1.3/docs/guide/extensions/versioning.html</a>.</p>
  40. *
  41. * @ant.task name="jarlib-manifest"
  42. */
  43. public final class JarLibManifestTask extends Task {
  44. /**
  45. * Version of manifest spec that task generates.
  46. */
  47. private static final String MANIFEST_VERSION = "1.0";
  48. /**
  49. * "Created-By" string used when creating manifest.
  50. */
  51. private static final String CREATED_BY = "Created-By";
  52. /**
  53. * The library to display information about.
  54. */
  55. private File destFile;
  56. /**
  57. * The extension supported by this library (if any).
  58. */
  59. private Extension extension;
  60. /**
  61. * ExtensionAdapter objects representing
  62. * dependencies required by library.
  63. */
  64. private final ArrayList dependencies = new ArrayList();
  65. /**
  66. * ExtensionAdapter objects representing optional
  67. * dependencies required by library.
  68. */
  69. private final ArrayList optionals = new ArrayList();
  70. /**
  71. * Extra attributes the user specifies for main section
  72. * in manifest.
  73. */
  74. private final ArrayList extraAttributes = new ArrayList();
  75. /**
  76. * The location where generated manifest is placed.
  77. *
  78. * @param destFile The location where generated manifest is placed.
  79. */
  80. public void setDestfile(final File destFile) {
  81. this.destFile = destFile;
  82. }
  83. /**
  84. * Adds an extension that this library implements.
  85. *
  86. * @param extensionAdapter an extension that this library implements.
  87. *
  88. * @throws BuildException if there is multiple extensions detected
  89. * in the library.
  90. */
  91. public void addConfiguredExtension(final ExtensionAdapter extensionAdapter)
  92. throws BuildException {
  93. if (null != extension) {
  94. final String message =
  95. "Can not have multiple extensions defined in one library.";
  96. throw new BuildException(message);
  97. } else {
  98. extension = extensionAdapter.toExtension();
  99. }
  100. }
  101. /**
  102. * Adds a set of extensions that this library requires.
  103. *
  104. * @param extensionSet a set of extensions that this library requires.
  105. */
  106. public void addConfiguredDepends(final ExtensionSet extensionSet) {
  107. dependencies.add(extensionSet);
  108. }
  109. /**
  110. * Adds a set of extensions that this library optionally requires.
  111. *
  112. * @param extensionSet a set of extensions that this library optionally requires.
  113. */
  114. public void addConfiguredOptions(final ExtensionSet extensionSet) {
  115. optionals.add(extensionSet);
  116. }
  117. /**
  118. * Adds an attribute that is to be put in main section of manifest.
  119. *
  120. * @param attribute an attribute that is to be put in main section of manifest.
  121. */
  122. public void addConfiguredAttribute(final ExtraAttribute attribute) {
  123. extraAttributes.add(attribute);
  124. }
  125. /**
  126. * Execute the task.
  127. *
  128. * @throws BuildException if the task fails.
  129. */
  130. public void execute() throws BuildException {
  131. validate();
  132. final Manifest manifest = new Manifest();
  133. final Attributes attributes = manifest.getMainAttributes();
  134. attributes.put(Attributes.Name.MANIFEST_VERSION, MANIFEST_VERSION);
  135. final String createdBy = "Apache Ant " + getProject().getProperty("ant.version");
  136. attributes.putValue(CREATED_BY, createdBy);
  137. appendExtraAttributes(attributes);
  138. if (null != extension) {
  139. Extension.addExtension(extension, attributes);
  140. }
  141. //Add all the dependency data to manifest for dependencies
  142. final ArrayList depends = toExtensions(dependencies);
  143. appendExtensionList(attributes,
  144. Extension.EXTENSION_LIST,
  145. "lib",
  146. depends.size());
  147. appendLibraryList(attributes, "lib", depends);
  148. //Add all the dependency data to manifest for "optional"
  149. //dependencies
  150. final ArrayList option = toExtensions(optionals);
  151. appendExtensionList(attributes,
  152. Extension.OPTIONAL_EXTENSION_LIST,
  153. "opt",
  154. option.size());
  155. appendLibraryList(attributes, "opt", option);
  156. try {
  157. final String message = "Generating manifest " + destFile.getAbsoluteFile();
  158. log(message, Project.MSG_INFO);
  159. writeManifest(manifest);
  160. } catch (final IOException ioe) {
  161. throw new BuildException(ioe.getMessage(), ioe);
  162. }
  163. }
  164. /**
  165. * Validate the tasks parameters.
  166. *
  167. * @throws BuildException if invalid parameters found
  168. */
  169. private void validate() throws BuildException {
  170. if (null == destFile) {
  171. final String message = "Destfile attribute not specified.";
  172. throw new BuildException(message);
  173. }
  174. if (destFile.exists() && !destFile.isFile()) {
  175. final String message = destFile + " is not a file.";
  176. throw new BuildException(message);
  177. }
  178. }
  179. /**
  180. * Add any extra attributes to the manifest.
  181. *
  182. * @param attributes the manifest section to write
  183. * attributes to
  184. */
  185. private void appendExtraAttributes(final Attributes attributes) {
  186. final Iterator iterator = extraAttributes.iterator();
  187. while (iterator.hasNext()) {
  188. final ExtraAttribute attribute =
  189. (ExtraAttribute) iterator.next();
  190. attributes.putValue(attribute.getName(),
  191. attribute.getValue());
  192. }
  193. }
  194. /**
  195. * Write out manifest to destfile.
  196. *
  197. * @param manifest the manifest
  198. * @throws IOException if error writing file
  199. */
  200. private void writeManifest(final Manifest manifest)
  201. throws IOException {
  202. FileOutputStream output = null;
  203. try {
  204. output = new FileOutputStream(destFile);
  205. manifest.write(output);
  206. output.flush();
  207. } finally {
  208. if (null != output) {
  209. try {
  210. output.close();
  211. } catch (IOException e) {
  212. // ignore
  213. }
  214. }
  215. }
  216. }
  217. /**
  218. * Append specified extensions to specified attributes.
  219. * Use the extensionKey to list the extensions, usually "Extension-List:"
  220. * for required dependencies and "Optional-Extension-List:" for optional
  221. * dependencies. NOTE: "Optional" dependencies are not part of the
  222. * specification.
  223. *
  224. * @param attributes the attributes to add extensions to
  225. * @param extensions the list of extensions
  226. * @throws BuildException if an error occurs
  227. */
  228. private void appendLibraryList(final Attributes attributes,
  229. final String listPrefix,
  230. final ArrayList extensions)
  231. throws BuildException {
  232. final int size = extensions.size();
  233. for (int i = 0; i < size; i++) {
  234. final Extension extension = (Extension) extensions.get(i);
  235. final String prefix = listPrefix + i + "-";
  236. Extension.addExtension(extension, prefix, attributes);
  237. }
  238. }
  239. /**
  240. * Append an attribute such as "Extension-List: lib0 lib1 lib2"
  241. * using specified prefix and counting up to specified size.
  242. * Also use specified extensionKey so that can generate list of
  243. * optional dependencies aswell.
  244. *
  245. * @param size the number of librarys to list
  246. * @param listPrefix the prefix for all librarys
  247. * @param attributes the attributes to add key-value to
  248. * @param extensionKey the key to use
  249. */
  250. private void appendExtensionList(final Attributes attributes,
  251. final Attributes.Name extensionKey,
  252. final String listPrefix,
  253. final int size) {
  254. final StringBuffer sb = new StringBuffer();
  255. for (int i = 0; i < size; i++) {
  256. sb.append(listPrefix + i);
  257. sb.append(' ');
  258. }
  259. //add in something like
  260. //"Extension-List: javahelp java3d"
  261. attributes.put(extensionKey, sb.toString());
  262. }
  263. /**
  264. * Convert a list of ExtensionSet objects to extensions.
  265. *
  266. * @param extensionSets the list of ExtensionSets to add to list
  267. * @throws BuildException if an error occurs
  268. */
  269. private ArrayList toExtensions(final ArrayList extensionSets)
  270. throws BuildException {
  271. final ArrayList results = new ArrayList();
  272. final int size = extensionSets.size();
  273. for (int i = 0; i < size; i++) {
  274. final ExtensionSet set = (ExtensionSet) extensionSets.get(i);
  275. final Extension[] extensions = set.toExtensions(getProject());
  276. for (int j = 0; j < extensions.length; j++) {
  277. results.add(extensions[ j ]);
  278. }
  279. }
  280. return results;
  281. }
  282. }