1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package com.finalist.tools.ant.taskdefs;
19
20 import org.apache.tools.ant.BuildException;
21 import org.apache.tools.ant.Task;
22 import org.apache.tools.ant.taskdefs.Copy;
23 import org.apache.tools.ant.taskdefs.Cvs;
24 import org.apache.tools.ant.taskdefs.Delete;
25 import org.apache.tools.ant.taskdefs.Get;
26 import org.apache.tools.ant.taskdefs.Mkdir;
27 import org.w3c.dom.Document;
28 import org.w3c.dom.NamedNodeMap;
29 import org.w3c.dom.Node;
30 import org.w3c.dom.NodeList;
31 import org.xml.sax.SAXException;
32
33 import javax.xml.parsers.DocumentBuilder;
34 import javax.xml.parsers.DocumentBuilderFactory;
35 import javax.xml.parsers.ParserConfigurationException;
36 import java.io.File;
37 import java.io.FileFilter;
38 import java.io.FileInputStream;
39 import java.io.FileOutputStream;
40 import java.io.IOException;
41 import java.net.MalformedURLException;
42 import java.net.URL;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.HashSet;
46 import java.util.Iterator;
47 import java.util.LinkedList;
48 import java.util.List;
49 import java.util.Properties;
50
51
52 /***
53 * This task is used to checkout 3rd-party libraries. The libraries that are to be
54 * checked out are read from the libXmlFile specified in the build.xml file. They
55 * are checked out to the libDir which is also specified in the build.xml file. In
56 * the versions.properties file the tags of the checked out files are stored. If a
57 * stored tag does not comply with the tag specified in the libXmlFile, the file is
58 * deleted and checked out again to get the correct version. Files that are removed
59 * from the libXmlFile but still exist in the libDir will be removed.
60 * <p>
61 * An individual library may be fetched from one of two kinds of source:
62 * <li>the library is checked out from a CVS server, or</li>
63 * <li>the library is fetched from a URL.</li>
64 * The source if configured per library in the accompanying lib.xml file.
65 * If the library is fetched from a URL, no check is done on the server
66 * so it can be used locally.
67 * <p>
68 * Usage in build.xml:
69 * <p><code>
70 * <target description="CVS-checkout for needed JARs." name="checkout.lib" depends="init.ant"><br>
71 * <taskdef name="checkout" <br>
72 * classname="com.finalist.tools.ant.taskdefs.CheckoutLibTask" <br>
73 * classpathref="ant.classpath"/> <br>
74 * <checkout libXmlFile="lib.xml" libDir="${lib.dir}"/> <br>
75 * </target> <br>
76 * </code>
77 */
78 public class CheckoutLibTask extends Task {
79
80 /*** destination directory */
81 private File libDir;
82 /*** Libraries which are read from the libXmlFile */
83 private List libs = new LinkedList();
84 private List localLibs = new LinkedList();
85 private File libXmlFile;
86 private File tmpDir;
87 /*** The name of the properties file created by this task, containing the latest checked-out lib versions. */
88 public final static String LIBVERSION_PROPERTIES_FILENAME = "libversion.properties";
89
90
91 /*** Method which is called by ant to execute the task.
92 * @throws BuildException Exception which is thrown to stop the build process and display an error message
93 */
94 public void execute() throws BuildException {
95 Properties props = new Properties();
96 File versionPropFile = null;
97
98 try {
99 try {
100 readXML();
101
102 }
103 catch (Exception e) {
104 e.printStackTrace();
105 throw new BuildException("Error reading the library xml!");
106 }
107 if (libDir == null) {
108 libDir = new File("lib");
109 }
110 if (!libDir.exists()) {
111 libDir.mkdirs();
112 }
113 if (!libDir.isDirectory()) {
114 throw new BuildException("Destination directory not properly set.");
115 }
116 emptyTempDir();
117
118 HashSet okLibs = new HashSet();
119 versionPropFile = new File(libDir, LIBVERSION_PROPERTIES_FILENAME);
120 if (!versionPropFile.exists()) {
121 versionPropFile.createNewFile();
122 }
123 props.load(new FileInputStream(versionPropFile));
124 List existingLibFiles = new ArrayList(Arrays.asList(libDir.listFiles(new FileFilter() {
125 public boolean accept(File file) {
126 return !file.isDirectory();
127 }
128 })));
129
130
131
132 existingLibFiles.remove(new File(libDir, "lib.dtd"));
133 existingLibFiles.remove(new File(libDir, "lib.xml"));
134 existingLibFiles.remove(new File(libDir, LIBVERSION_PROPERTIES_FILENAME));
135 existingLibFiles.remove(new File(libDir, ".cvsignore"));
136
137 Iterator libIterator = libs.iterator();
138 while (libIterator.hasNext()) {
139
140
141 props.store(new FileOutputStream(versionPropFile), "Current versions of 3rd-party libraries");
142
143 Lib lib = (Lib) libIterator.next();
144 String filename = lib.getFilename();
145 String versionTag = props.getProperty(filename);
146 File existingLibFile = new File(libDir, filename);
147 if (!existingLibFiles.contains(existingLibFile) &&
148 !okLibs.contains(existingLibFile)) {
149
150 checkout(lib);
151 okLibs.add(existingLibFile);
152 props.setProperty(filename, lib.getVersion());
153 continue;
154 }
155
156
157
158 existingLibFiles.remove(existingLibFile);
159 if ( (versionTag == null || !versionTag.equals(lib.getVersion())) && (lib.getVersion().indexOf("://") == -1)) {
160
161 existingLibFile.delete();
162 checkout(lib);
163 props.setProperty(filename, lib.getVersion());
164 }
165 else {
166 log(filename + " up to date.");
167 okLibs.add(existingLibFile);
168 }
169 }
170
171 Iterator localLibIterator = localLibs.iterator();
172 while (localLibIterator.hasNext()) {
173 String filename = (String) localLibIterator.next();
174 log("keeping " + filename + " for testing puposes");
175 log("!!!!! check in in CVS and update lib.xml if you check in any source that uses this library !!!!!");
176 existingLibFiles.remove(new File(libDir, filename));
177 props.remove(filename);
178 }
179
180 Iterator existingLibFileIterator = existingLibFiles.iterator();
181 while (existingLibFileIterator.hasNext()) {
182 File existingLibFile = (File) existingLibFileIterator.next();
183 String filename = existingLibFile.getName();
184 boolean deleted = existingLibFile.delete();
185 if (!deleted) {
186 throw new BuildException("Deletion of " + filename + " failed!!!!!!!!!!\n" +
187 filename + " is probably part of a classpath which is used by the checkoutLibTask.\n" +
188 "Check the buildfile. The classpath should only contain finalist-ant.jar");
189 }
190 else
191 log("Deleted " + existingLibFile.getAbsolutePath());
192 props.remove(filename);
193 }
194
195 Delete delete = (Delete) project.createTask("delete");
196 delete.setDir(tmpDir);
197 delete.execute();
198
199 }
200 catch (IOException ie) {
201 ie.printStackTrace();
202 throw new BuildException("Error reading/writing files!");
203 }
204 }
205
206
207 /*** Optional destination directory, defaults to "lib".
208 * @param libDir Directory in which the checked out libraries should be stored.
209 */
210 public void setLibDir(File libDir) {
211
212 this.libDir = libDir;
213 }
214
215
216 /*** Setter for property libFile.
217 * @param libXmlFile The lib file which specifies what files are to be checked out
218 */
219 public void setLibXmlFile(File libXmlFile) {
220 this.libXmlFile = libXmlFile;
221 }
222
223
224 /*** Setter for property tmpDir.
225 * @param tmpDir New value of property tmpDir.
226 *
227 */
228 public void setTmpDir(File tmpDir) {
229 this.tmpDir = tmpDir;
230 }
231
232
233 /*** Reads the libraries and their tags from the libXmlFile, stores the data in Lib
234 * classes and adds these classes to the libs List.
235 * @throws ParserConfigurationException Indicates a serious configuration error.
236 * @throws SAXException Encapsulate a general SAX error or warning.
237 * @throws IOException Signals that an I/O exception of some sort has occurred
238 */
239 private void readXML() throws ParserConfigurationException, SAXException, IOException {
240 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
241 dbf.setValidating(true);
242 DocumentBuilder db = dbf.newDocumentBuilder();
243 Document doc = db.parse(libXmlFile);
244 NodeList nl = doc.getElementsByTagName("lib");
245 for (int i = 0; i < nl.getLength(); i++) {
246 Node node = nl.item(i);
247 NamedNodeMap atts = node.getAttributes();
248 String version;
249 String file;
250 Node url = atts.getNamedItem("url");
251 if (url != null) {
252 file = url.getNodeValue();
253 version = file;
254 }
255 else {
256 version = atts.getNamedItem("version").getNodeValue();
257 file = atts.getNamedItem("file").getNodeValue();
258 }
259 Lib lib = new Lib();
260 lib.setVersion(version);
261 lib.setFile(file);
262 libs.add(lib);
263 }
264 nl = doc.getElementsByTagName("localLib");
265 for (int i = 0; i < nl.getLength(); i++) {
266 Node node = nl.item(i);
267 NamedNodeMap atts = node.getAttributes();
268 String file = atts.getNamedItem("file").getNodeValue();
269 localLibs.add(file);
270 }
271
272 }
273
274
275 /*** Checks out a library
276 * @param lib Library to be checked out
277 */
278 private void checkout(Lib lib) {
279 if (lib.getVersion().indexOf("://") != -1) {
280 getFromURL(lib);
281 }
282 else {
283 log("File: " + lib.getFile() + "\n" +
284 "Tag: " + lib.getVersion() + "\n" +
285 "checking out to " + libDir.getAbsolutePath());
286 Cvs cvs = (Cvs) project.createTask("cvs");
287 cvs.setFailOnError(true);
288 cvs.setCommand("export -r " + lib.getVersion() +
289 " -d " + tmpDir.getName() +
290 " " + lib.getFile());
291 cvs.execute();
292
293 Copy copy = (Copy) project.createTask("copy");
294 copy.setFile(new File(tmpDir, lib.getFilename()));
295 copy.setTodir(libDir);
296 copy.setOverwrite(true);
297 copy.execute();
298 }
299 }
300
301
302 /*** Gets a library via HTTP from a URL.
303 * @param lib Library to be checked out
304 */
305 private void getFromURL(Lib lib) {
306 log("URL: " + lib.getFile() + "\n" +
307 "checking out to " + libDir.getAbsolutePath());
308 Get get = (Get) project.createTask("get");
309 try {
310 URL url = new URL(lib.getFile());
311 get.setSrc(url);
312 get.setDest(new File(libDir, new File(url.getFile()).getName()));
313 get.execute();
314
315 }
316 catch (MalformedURLException e) {
317 log("ERROR! Can't get library from invalid URL: " + lib.getFile());
318 }
319 }
320
321
322 /*** Deletes all files whithin the temporary directory if any exist. This is
323 * necessary to delete the directory, or to checkout files which allready exist
324 * within the temporary directory.
325 */
326 private void emptyTempDir() {
327 Delete delete = (Delete) project.createTask("delete");
328 delete.setDir(tmpDir);
329 delete.execute();
330 Mkdir mkdir = (Mkdir) project.createTask("mkdir");
331 mkdir.setDir(tmpDir);
332 mkdir.execute();
333 }
334
335 }