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: *
017: */
018:
019: package org.apache.tools.ant.taskdefs;
020:
021: import java.io.File;
022: import org.apache.tools.ant.AntClassLoader;
023: import org.apache.tools.ant.BuildException;
024: import org.apache.tools.ant.Project;
025: import org.apache.tools.ant.Task;
026: import org.apache.tools.ant.taskdefs.condition.Condition;
027: import org.apache.tools.ant.types.EnumeratedAttribute;
028: import org.apache.tools.ant.types.Path;
029: import org.apache.tools.ant.types.Reference;
030: import org.apache.tools.ant.util.FileUtils;
031: import org.apache.tools.ant.util.StringUtils;
032:
033: /**
034: * Will set the given property if the requested resource is available at
035: * runtime. This task may also be used as a condition by the condition task.
036: *
037: * @since Ant 1.1
038: *
039: * @ant.task category="control"
040: */
041: public class Available extends Task implements Condition {
042: private static final FileUtils FILE_UTILS = FileUtils
043: .getFileUtils();
044:
045: private String property;
046: private String classname;
047: private String filename;
048: private File file;
049: private Path filepath;
050: private String resource;
051: private FileDir type;
052: private Path classpath;
053: private AntClassLoader loader;
054: private String value = "true";
055: private boolean isTask = false;
056: private boolean ignoreSystemclasses = false;
057: private boolean searchParents = false;
058:
059: /**
060: * Set the searchParents attribute.
061: * This controls the behaviour of the the "file" type.
062: * If true, the path, parent path and grandparent path are
063: * searched for the file. If false, only the path is seached.
064: * The default value is false.
065: * @param searchParents the value to set.
066: */
067: public void setSearchParents(boolean searchParents) {
068: this .searchParents = searchParents;
069: }
070:
071: /**
072: * Set the classpath to be used when searching for classes and resources.
073: *
074: * @param classpath an Ant Path object containing the search path.
075: */
076: public void setClasspath(Path classpath) {
077: createClasspath().append(classpath);
078: }
079:
080: /**
081: * Classpath to be used when searching for classes and resources.
082: *
083: * @return an empty Path instance to be configured by Ant.
084: */
085: public Path createClasspath() {
086: if (this .classpath == null) {
087: this .classpath = new Path(getProject());
088: }
089: return this .classpath.createPath();
090: }
091:
092: /**
093: * Set the classpath by reference.
094: *
095: * @param r a Reference to a Path instance to be used as the classpath
096: * value.
097: */
098: public void setClasspathRef(Reference r) {
099: createClasspath().setRefid(r);
100: }
101:
102: /**
103: * Set the path to use when looking for a file.
104: *
105: * @param filepath a Path instance containing the search path for files.
106: */
107: public void setFilepath(Path filepath) {
108: createFilepath().append(filepath);
109: }
110:
111: /**
112: * Path to search for file resources.
113: *
114: * @return a new Path instance which Ant will configure with a file search
115: * path.
116: */
117: public Path createFilepath() {
118: if (this .filepath == null) {
119: this .filepath = new Path(getProject());
120: }
121: return this .filepath.createPath();
122: }
123:
124: /**
125: * Set the name of the property which will be set if the particular resource
126: * is available.
127: *
128: * @param property the name of the property to set.
129: */
130: public void setProperty(String property) {
131: this .property = property;
132: }
133:
134: /**
135: * Set the value to be given to the property if the desired resource is
136: * available.
137: *
138: * @param value the value to be given.
139: */
140: public void setValue(String value) {
141: this .value = value;
142: }
143:
144: /**
145: * Set a classname of a class which must be available to set the given
146: * property.
147: *
148: * @param classname the name of the class required.
149: */
150: public void setClassname(String classname) {
151: if (!"".equals(classname)) {
152: this .classname = classname;
153: }
154: }
155:
156: /**
157: * Set the file which must be present in the file system to set the given
158: * property.
159: *
160: * @param file the name of the file which is required.
161: */
162: public void setFile(File file) {
163: this .file = file;
164: this .filename = FILE_UTILS.removeLeadingPath(getProject()
165: .getBaseDir(), file);
166: }
167:
168: /**
169: * Set the name of a Java resource which is required to set the property.
170: *
171: * @param resource the name of a resource which is required to be available.
172: */
173: public void setResource(String resource) {
174: this .resource = resource;
175: }
176:
177: /**
178: * @deprecated since 1.5.x.
179: * setType(String) is deprecated and is replaced with
180: * setType(Available.FileDir) to make Ant's Introspection
181: * mechanism do the work and also to encapsulate operations on
182: * the type in its own class.
183: * @param type the type of resource
184: */
185: public void setType(String type) {
186: log(
187: "DEPRECATED - The setType(String) method has been deprecated."
188: + " Use setType(Available.FileDir) instead.",
189: Project.MSG_WARN);
190: this .type = new FileDir();
191: this .type.setValue(type);
192: }
193:
194: /**
195: * Set what type of file is required - either directory or file.
196: *
197: * @param type an instance of the FileDir enumeratedAttribute indicating
198: * whether the file required is to be a directory or a plain
199: * file.
200: */
201: public void setType(FileDir type) {
202: this .type = type;
203: }
204:
205: /**
206: * Set whether the search for classes should ignore the runtime classes and
207: * just use the given classpath.
208: *
209: * @param ignore true if system classes are to be ignored.
210: */
211: public void setIgnoresystemclasses(boolean ignore) {
212: this .ignoreSystemclasses = ignore;
213: }
214:
215: /**
216: * Entry point when operating as a task.
217: *
218: * @exception BuildException if the task is not configured correctly.
219: */
220: public void execute() throws BuildException {
221: if (property == null) {
222: throw new BuildException("property attribute is required",
223: getLocation());
224: }
225:
226: isTask = true;
227: try {
228: if (eval()) {
229: String oldvalue = getProject().getProperty(property);
230: if (null != oldvalue && !oldvalue.equals(value)) {
231: log(
232: "DEPRECATED - <available> used to override an existing"
233: + " property."
234: + StringUtils.LINE_SEP
235: + " Build file should not reuse the same property"
236: + " name for different values.",
237: Project.MSG_WARN);
238: }
239: // NB: this makes use of Project#setProperty rather than Project#setNewProperty
240: // due to backwards compatiblity reasons
241: getProject().setProperty(property, value);
242: }
243: } finally {
244: isTask = false;
245: }
246: }
247:
248: /**
249: * Evaluate the availability of a resource.
250: *
251: * @return boolean is the resource is available.
252: * @exception BuildException if the condition is not configured correctly
253: */
254: public boolean eval() throws BuildException {
255: try {
256: if (classname == null && file == null && resource == null) {
257: throw new BuildException(
258: "At least one of (classname|file|"
259: + "resource) is required",
260: getLocation());
261: }
262: if (type != null) {
263: if (file == null) {
264: throw new BuildException(
265: "The type attribute is only valid "
266: + "when specifying the file "
267: + "attribute.", getLocation());
268: }
269: }
270: if (classpath != null) {
271: classpath.setProject(getProject());
272: this .loader = getProject().createClassLoader(classpath);
273: }
274: String appendix = "";
275: if (isTask) {
276: appendix = " to set property " + property;
277: } else {
278: setTaskName("available");
279: }
280: if ((classname != null) && !checkClass(classname)) {
281: log("Unable to load class " + classname + appendix,
282: Project.MSG_VERBOSE);
283: return false;
284: }
285: if ((file != null) && !checkFile()) {
286: StringBuffer buf = new StringBuffer("Unable to find ");
287: if (type != null) {
288: buf.append(type).append(' ');
289: }
290: buf.append(filename).append(appendix);
291: log(buf.toString(), Project.MSG_VERBOSE);
292: return false;
293: }
294: if ((resource != null) && !checkResource(resource)) {
295: log("Unable to load resource " + resource + appendix,
296: Project.MSG_VERBOSE);
297: return false;
298: }
299: } finally {
300: if (loader != null) {
301: loader.cleanup();
302: loader = null;
303: }
304: if (!isTask) {
305: setTaskName(null);
306: }
307: }
308: return true;
309: }
310:
311: /**
312: * Search for file/directory either relative to project's
313: * basedir or in the path given as filepath.
314: *
315: * <p>filepath can be a list of directory and/or file names (gen'd
316: * via <fileset>)</p>
317: *
318: * <p>look for:</p><ul>
319: * <li>full-pathname specified == path in list</li>
320: * <li>full-pathname specified == parent dir of path in list</li>
321: * <li>simple name specified == path in list</li>
322: * <li>simple name specified == path in list + name</li>
323: * <li>simple name specified == parent dir + name</li>
324: * <li>simple name specified == parent of parent dir + name</li>
325: * </ul>
326: */
327: private boolean checkFile() {
328: if (filepath == null) {
329: return checkFile(file, filename);
330: } else {
331: String[] paths = filepath.list();
332: for (int i = 0; i < paths.length; ++i) {
333: log("Searching " + paths[i], Project.MSG_DEBUG);
334: File path = new File(paths[i]);
335:
336: // ** full-pathname specified == path in list
337: // ** simple name specified == path in list
338: if (path.exists() && filename.equals(paths[i])) {
339: if (type == null) {
340: log("Found: " + path, Project.MSG_VERBOSE);
341: return true;
342: } else if (type.isDir() && path.isDirectory()) {
343: log("Found directory: " + path,
344: Project.MSG_VERBOSE);
345: return true;
346: } else if (type.isFile() && path.isFile()) {
347: log("Found file: " + path, Project.MSG_VERBOSE);
348: return true;
349: }
350: // not the requested type
351: return false;
352: }
353: File parent = path.getParentFile();
354: // ** full-pathname specified == parent dir of path in list
355: if (parent != null && parent.exists()
356: && filename.equals(parent.getAbsolutePath())) {
357: if (type == null) {
358: log("Found: " + parent, Project.MSG_VERBOSE);
359: return true;
360: } else if (type.isDir()) {
361: log("Found directory: " + parent,
362: Project.MSG_VERBOSE);
363: return true;
364: }
365: // not the requested type
366: return false;
367: }
368: // ** simple name specified == path in list + name
369: if (path.exists() && path.isDirectory()) {
370: if (checkFile(new File(path, filename), filename
371: + " in " + path)) {
372: return true;
373: }
374: }
375: // ** simple name specified == parent dir + name
376: while (searchParents && parent != null
377: && parent.exists()) {
378: if (checkFile(new File(parent, filename), filename
379: + " in " + parent)) {
380: return true;
381: }
382: parent = parent.getParentFile();
383: }
384: }
385: }
386: return false;
387: }
388:
389: /**
390: * Check if a given file exists and matches the required type.
391: */
392: private boolean checkFile(File f, String text) {
393: if (type != null) {
394: if (type.isDir()) {
395: if (f.isDirectory()) {
396: log("Found directory: " + text, Project.MSG_VERBOSE);
397: }
398: return f.isDirectory();
399: } else if (type.isFile()) {
400: if (f.isFile()) {
401: log("Found file: " + text, Project.MSG_VERBOSE);
402: }
403: return f.isFile();
404: }
405: }
406: if (f.exists()) {
407: log("Found: " + text, Project.MSG_VERBOSE);
408: }
409: return f.exists();
410: }
411:
412: /**
413: * Check if a given resource can be loaded.
414: */
415: private boolean checkResource(String resource) {
416: if (loader != null) {
417: return (loader.getResourceAsStream(resource) != null);
418: } else {
419: ClassLoader cL = this .getClass().getClassLoader();
420: if (cL != null) {
421: return (cL.getResourceAsStream(resource) != null);
422: } else {
423: return (ClassLoader.getSystemResourceAsStream(resource) != null);
424: }
425: }
426: }
427:
428: /**
429: * Check if a given class can be loaded.
430: */
431: private boolean checkClass(String classname) {
432: try {
433: if (ignoreSystemclasses) {
434: loader = getProject().createClassLoader(classpath);
435: loader.setParentFirst(false);
436: loader.addJavaLibraries();
437: if (loader != null) {
438: try {
439: loader.findClass(classname);
440: } catch (SecurityException se) {
441: // class found but restricted name; this is
442: // actually the case we're looking for in JDK 1.3+,
443: // so catch the exception and return
444: return true;
445: }
446: } else {
447: return false;
448: }
449: } else if (loader != null) {
450: loader.loadClass(classname);
451: } else {
452: ClassLoader l = this .getClass().getClassLoader();
453: // Can return null to represent the bootstrap class loader.
454: // see API docs of Class.getClassLoader.
455: if (l != null) {
456: Class.forName(classname, true, l);
457: } else {
458: Class.forName(classname);
459: }
460: }
461: return true;
462: } catch (ClassNotFoundException e) {
463: log("class \"" + classname + "\" was not found",
464: Project.MSG_DEBUG);
465: return false;
466: } catch (NoClassDefFoundError e) {
467: log("Could not load dependent class \"" + e.getMessage()
468: + "\" for class \"" + classname + "\"",
469: Project.MSG_DEBUG);
470: return false;
471: }
472: }
473:
474: /**
475: * EnumeratedAttribute covering the file types to be checked for, either
476: * file or dir.
477: */
478: public static class FileDir extends EnumeratedAttribute {
479:
480: private static final String[] VALUES = { "file", "dir" };
481:
482: /**
483: * @see EnumeratedAttribute#getValues
484: */
485: /** {@inheritDoc}. */
486: public String[] getValues() {
487: return VALUES;
488: }
489:
490: /**
491: * Indicate if the value specifies a directory.
492: *
493: * @return true if the value specifies a directory.
494: */
495: public boolean isDir() {
496: return "dir".equalsIgnoreCase(getValue());
497: }
498:
499: /**
500: * Indicate if the value specifies a file.
501: *
502: * @return true if the value specifies a file.
503: */
504: public boolean isFile() {
505: return "file".equalsIgnoreCase(getValue());
506: }
507:
508: }
509: }
|