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;
|
---|
18 |
|
---|
19 | import java.io.ByteArrayOutputStream;
|
---|
20 | import java.io.File;
|
---|
21 | import java.io.FileInputStream;
|
---|
22 | import java.io.FileNotFoundException;
|
---|
23 | import java.io.FileOutputStream;
|
---|
24 | import java.io.IOException;
|
---|
25 | import java.io.OutputStream;
|
---|
26 | import java.io.OutputStreamWriter;
|
---|
27 | import java.io.Writer;
|
---|
28 | import java.util.Enumeration;
|
---|
29 | import java.util.Hashtable;
|
---|
30 | import java.util.Properties;
|
---|
31 | import java.util.Vector;
|
---|
32 | import javax.xml.parsers.DocumentBuilder;
|
---|
33 | import javax.xml.parsers.DocumentBuilderFactory;
|
---|
34 | import org.apache.tools.ant.BuildException;
|
---|
35 | import org.apache.tools.ant.Project;
|
---|
36 | import org.apache.tools.ant.Task;
|
---|
37 | import org.apache.tools.ant.types.EnumeratedAttribute;
|
---|
38 | import org.apache.tools.ant.types.PropertySet;
|
---|
39 | import org.apache.tools.ant.util.CollectionUtils;
|
---|
40 | import org.apache.tools.ant.util.DOMElementWriter;
|
---|
41 | import org.w3c.dom.Document;
|
---|
42 | import org.w3c.dom.Element;
|
---|
43 |
|
---|
44 | /**
|
---|
45 | * Displays all the current properties in the build. The output can be sent to
|
---|
46 | * a file if desired. <P>
|
---|
47 | *
|
---|
48 | * Attribute "destfile" defines a file to send the properties to. This can be
|
---|
49 | * processed as a standard property file later. <P>
|
---|
50 | *
|
---|
51 | * Attribute "prefix" defines a prefix which is used to filter the properties
|
---|
52 | * only those properties starting with this prefix will be echoed. <P>
|
---|
53 | *
|
---|
54 | * By default, the "failonerror" attribute is enabled. If an error occurs while
|
---|
55 | * writing the properties to a file, and this attribute is enabled, then a
|
---|
56 | * BuildException will be thrown. If disabled, then IO errors will be reported
|
---|
57 | * as a log statement, but no error will be thrown. <P>
|
---|
58 | *
|
---|
59 | * Examples: <pre>
|
---|
60 | * <echoproperties />
|
---|
61 | * </pre> Report the current properties to the log. <P>
|
---|
62 | *
|
---|
63 | * <pre>
|
---|
64 | * <echoproperties destfile="my.properties" />
|
---|
65 | * </pre> Report the current properties to the file "my.properties", and will
|
---|
66 | * fail the build if the file could not be created or written to. <P>
|
---|
67 | *
|
---|
68 | * <pre>
|
---|
69 | * <echoproperties destfile="my.properties" failonerror="false"
|
---|
70 | * prefix="ant" />
|
---|
71 | * </pre> Report all properties beginning with 'ant' to the file
|
---|
72 | * "my.properties", and will log a message if the file could not be created or
|
---|
73 | * written to, but will still allow the build to continue.
|
---|
74 | *
|
---|
75 | *@since Ant 1.5
|
---|
76 | */
|
---|
77 | public class EchoProperties extends Task {
|
---|
78 |
|
---|
79 | /**
|
---|
80 | * the properties element.
|
---|
81 | */
|
---|
82 | private static final String PROPERTIES = "properties";
|
---|
83 |
|
---|
84 | /**
|
---|
85 | * the property element.
|
---|
86 | */
|
---|
87 | private static final String PROPERTY = "property";
|
---|
88 |
|
---|
89 | /**
|
---|
90 | * name attribute for property, testcase and testsuite elements.
|
---|
91 | */
|
---|
92 | private static final String ATTR_NAME = "name";
|
---|
93 |
|
---|
94 | /**
|
---|
95 | * value attribute for property elements.
|
---|
96 | */
|
---|
97 | private static final String ATTR_VALUE = "value";
|
---|
98 |
|
---|
99 | /**
|
---|
100 | * the input file.
|
---|
101 | */
|
---|
102 | private File inFile = null;
|
---|
103 |
|
---|
104 | /**
|
---|
105 | * File object pointing to the output file. If this is null, then
|
---|
106 | * we output to the project log, not to a file.
|
---|
107 | */
|
---|
108 | private File destfile = null;
|
---|
109 |
|
---|
110 | /**
|
---|
111 | * If this is true, then errors generated during file output will become
|
---|
112 | * build errors, and if false, then such errors will be logged, but not
|
---|
113 | * thrown.
|
---|
114 | */
|
---|
115 | private boolean failonerror = true;
|
---|
116 |
|
---|
117 | private Vector propertySets = new Vector();
|
---|
118 |
|
---|
119 | private String format = "text";
|
---|
120 |
|
---|
121 | /**
|
---|
122 | * Sets the input file.
|
---|
123 | *
|
---|
124 | * @param file the input file
|
---|
125 | */
|
---|
126 | public void setSrcfile(File file) {
|
---|
127 | inFile = file;
|
---|
128 | }
|
---|
129 |
|
---|
130 | /**
|
---|
131 | * Set a file to store the property output. If this is never specified,
|
---|
132 | * then the output will be sent to the Ant log.
|
---|
133 | *
|
---|
134 | *@param destfile file to store the property output
|
---|
135 | */
|
---|
136 | public void setDestfile(File destfile) {
|
---|
137 | this.destfile = destfile;
|
---|
138 | }
|
---|
139 |
|
---|
140 |
|
---|
141 | /**
|
---|
142 | * If true, the task will fail if an error occurs writing the properties
|
---|
143 | * file, otherwise errors are just logged.
|
---|
144 | *
|
---|
145 | *@param failonerror <tt>true</tt> if IO exceptions are reported as build
|
---|
146 | * exceptions, or <tt>false</tt> if IO exceptions are ignored.
|
---|
147 | */
|
---|
148 | public void setFailOnError(boolean failonerror) {
|
---|
149 | this.failonerror = failonerror;
|
---|
150 | }
|
---|
151 |
|
---|
152 |
|
---|
153 | /**
|
---|
154 | * If the prefix is set, then only properties which start with this
|
---|
155 | * prefix string will be recorded. If this is never set, or it is set
|
---|
156 | * to an empty string or <tt>null</tt>, then all properties will be
|
---|
157 | * recorded. <P>
|
---|
158 | *
|
---|
159 | * For example, if the property is set as:
|
---|
160 | * <PRE><echoproperties prefix="ant." /></PRE>
|
---|
161 | * then the property "ant.home" will be recorded, but "ant-example"
|
---|
162 | * will not.
|
---|
163 | *
|
---|
164 | *@param prefix The new prefix value
|
---|
165 | */
|
---|
166 | public void setPrefix(String prefix) {
|
---|
167 | PropertySet ps = new PropertySet();
|
---|
168 | ps.setProject(getProject());
|
---|
169 | ps.appendPrefix(prefix);
|
---|
170 | addPropertyset(ps);
|
---|
171 | }
|
---|
172 |
|
---|
173 | /**
|
---|
174 | * A set of properties to write.
|
---|
175 | *
|
---|
176 | * @since Ant 1.6
|
---|
177 | */
|
---|
178 | public void addPropertyset(PropertySet ps) {
|
---|
179 | propertySets.addElement(ps);
|
---|
180 | }
|
---|
181 |
|
---|
182 | public void setFormat(FormatAttribute ea) {
|
---|
183 | format = ea.getValue();
|
---|
184 | }
|
---|
185 |
|
---|
186 | public static class FormatAttribute extends EnumeratedAttribute {
|
---|
187 | private String [] formats = new String[]{"xml", "text"};
|
---|
188 |
|
---|
189 | public String[] getValues() {
|
---|
190 | return formats;
|
---|
191 | }
|
---|
192 | }
|
---|
193 |
|
---|
194 | /**
|
---|
195 | * Run the task.
|
---|
196 | *
|
---|
197 | *@exception BuildException trouble, probably file IO
|
---|
198 | */
|
---|
199 | public void execute() throws BuildException {
|
---|
200 | //copy the properties file
|
---|
201 | Hashtable allProps = new Hashtable();
|
---|
202 |
|
---|
203 | /* load properties from file if specified, otherwise
|
---|
204 | use Ant's properties */
|
---|
205 | if (inFile == null && propertySets.size() == 0) {
|
---|
206 | // add ant properties
|
---|
207 | CollectionUtils.putAll(allProps, getProject().getProperties());
|
---|
208 | } else if (inFile != null) {
|
---|
209 | if (inFile.exists() && inFile.isDirectory()) {
|
---|
210 | String message = "srcfile is a directory!";
|
---|
211 | if (failonerror) {
|
---|
212 | throw new BuildException(message, getLocation());
|
---|
213 | } else {
|
---|
214 | log(message, Project.MSG_ERR);
|
---|
215 | }
|
---|
216 | return;
|
---|
217 | }
|
---|
218 |
|
---|
219 | if (inFile.exists() && !inFile.canRead()) {
|
---|
220 | String message = "Can not read from the specified srcfile!";
|
---|
221 | if (failonerror) {
|
---|
222 | throw new BuildException(message, getLocation());
|
---|
223 | } else {
|
---|
224 | log(message, Project.MSG_ERR);
|
---|
225 | }
|
---|
226 | return;
|
---|
227 | }
|
---|
228 |
|
---|
229 | FileInputStream in = null;
|
---|
230 | try {
|
---|
231 | in = new FileInputStream(inFile);
|
---|
232 | Properties props = new Properties();
|
---|
233 | props.load(in);
|
---|
234 | CollectionUtils.putAll(allProps, props);
|
---|
235 | } catch (FileNotFoundException fnfe) {
|
---|
236 | String message =
|
---|
237 | "Could not find file " + inFile.getAbsolutePath();
|
---|
238 | if (failonerror) {
|
---|
239 | throw new BuildException(message, fnfe, getLocation());
|
---|
240 | } else {
|
---|
241 | log(message, Project.MSG_WARN);
|
---|
242 | }
|
---|
243 | return;
|
---|
244 | } catch (IOException ioe) {
|
---|
245 | String message =
|
---|
246 | "Could not read file " + inFile.getAbsolutePath();
|
---|
247 | if (failonerror) {
|
---|
248 | throw new BuildException(message, ioe, getLocation());
|
---|
249 | } else {
|
---|
250 | log(message, Project.MSG_WARN);
|
---|
251 | }
|
---|
252 | return;
|
---|
253 | } finally {
|
---|
254 | try {
|
---|
255 | if (null != in) {
|
---|
256 | in.close();
|
---|
257 | }
|
---|
258 | } catch (IOException ioe) {
|
---|
259 | //ignore
|
---|
260 | }
|
---|
261 | }
|
---|
262 | }
|
---|
263 |
|
---|
264 | Enumeration e = propertySets.elements();
|
---|
265 | while (e.hasMoreElements()) {
|
---|
266 | PropertySet ps = (PropertySet) e.nextElement();
|
---|
267 | CollectionUtils.putAll(allProps, ps.getProperties());
|
---|
268 | }
|
---|
269 |
|
---|
270 | OutputStream os = null;
|
---|
271 | try {
|
---|
272 | if (destfile == null) {
|
---|
273 | os = new ByteArrayOutputStream();
|
---|
274 | saveProperties(allProps, os);
|
---|
275 | log(os.toString(), Project.MSG_INFO);
|
---|
276 | } else {
|
---|
277 | if (destfile.exists() && destfile.isDirectory()) {
|
---|
278 | String message = "destfile is a directory!";
|
---|
279 | if (failonerror) {
|
---|
280 | throw new BuildException(message, getLocation());
|
---|
281 | } else {
|
---|
282 | log(message, Project.MSG_ERR);
|
---|
283 | }
|
---|
284 | return;
|
---|
285 | }
|
---|
286 |
|
---|
287 | if (destfile.exists() && !destfile.canWrite()) {
|
---|
288 | String message =
|
---|
289 | "Can not write to the specified destfile!";
|
---|
290 | if (failonerror) {
|
---|
291 | throw new BuildException(message, getLocation());
|
---|
292 | } else {
|
---|
293 | log(message, Project.MSG_ERR);
|
---|
294 | }
|
---|
295 | return;
|
---|
296 | }
|
---|
297 | os = new FileOutputStream(this.destfile);
|
---|
298 | saveProperties(allProps, os);
|
---|
299 | }
|
---|
300 | } catch (IOException ioe) {
|
---|
301 | if (failonerror) {
|
---|
302 | throw new BuildException(ioe, getLocation());
|
---|
303 | } else {
|
---|
304 | log(ioe.getMessage(), Project.MSG_INFO);
|
---|
305 | }
|
---|
306 | } finally {
|
---|
307 | if (os != null) {
|
---|
308 | try {
|
---|
309 | os.close();
|
---|
310 | } catch (IOException ex) {
|
---|
311 | //ignore
|
---|
312 | }
|
---|
313 | }
|
---|
314 | }
|
---|
315 | }
|
---|
316 |
|
---|
317 |
|
---|
318 | /**
|
---|
319 | * Send the key/value pairs in the hashtable to the given output stream.
|
---|
320 | * Only those properties matching the <tt>prefix</tt> constraint will be
|
---|
321 | * sent to the output stream.
|
---|
322 | * The output stream will be closed when this method returns.
|
---|
323 | *
|
---|
324 | *@param allProps propfile to save
|
---|
325 | *@param os output stream
|
---|
326 | *@exception IOException trouble
|
---|
327 | */
|
---|
328 | protected void saveProperties(Hashtable allProps, OutputStream os)
|
---|
329 | throws IOException, BuildException {
|
---|
330 | Properties props = new Properties();
|
---|
331 | Enumeration e = allProps.keys();
|
---|
332 | while (e.hasMoreElements()) {
|
---|
333 | String name = e.nextElement().toString();
|
---|
334 | String value = allProps.get(name).toString();
|
---|
335 | props.put(name, value);
|
---|
336 | }
|
---|
337 |
|
---|
338 | if ("text".equals(format)) {
|
---|
339 | jdkSaveProperties(props, os, "Ant properties");
|
---|
340 | } else if ("xml".equals(format)) {
|
---|
341 | xmlSaveProperties(props, os);
|
---|
342 | }
|
---|
343 | }
|
---|
344 |
|
---|
345 | protected void xmlSaveProperties(Properties props,
|
---|
346 | OutputStream os) throws IOException {
|
---|
347 | // create XML document
|
---|
348 | Document doc = getDocumentBuilder().newDocument();
|
---|
349 | Element rootElement = doc.createElement(PROPERTIES);
|
---|
350 |
|
---|
351 | // output properties
|
---|
352 | String name;
|
---|
353 | Enumeration e = props.propertyNames();
|
---|
354 | while (e.hasMoreElements()) {
|
---|
355 | name = (String) e.nextElement();
|
---|
356 | Element propElement = doc.createElement(PROPERTY);
|
---|
357 | propElement.setAttribute(ATTR_NAME, name);
|
---|
358 | propElement.setAttribute(ATTR_VALUE, props.getProperty(name));
|
---|
359 | rootElement.appendChild(propElement);
|
---|
360 | }
|
---|
361 |
|
---|
362 | Writer wri = null;
|
---|
363 | try {
|
---|
364 | wri = new OutputStreamWriter(os, "UTF8");
|
---|
365 | wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
---|
366 | (new DOMElementWriter()).write(rootElement, wri, 0, "\t");
|
---|
367 | wri.flush();
|
---|
368 | } catch (IOException ioe) {
|
---|
369 | throw new BuildException("Unable to write XML file", ioe);
|
---|
370 | } finally {
|
---|
371 | if (wri != null) {
|
---|
372 | wri.close();
|
---|
373 | }
|
---|
374 | }
|
---|
375 | }
|
---|
376 |
|
---|
377 | /**
|
---|
378 | * JDK 1.2 allows for the safer method
|
---|
379 | * <tt>Properties.store(OutputStream, String)</tt>, which throws an
|
---|
380 | * <tt>IOException</tt> on an output error.
|
---|
381 | *
|
---|
382 | *@param props the properties to record
|
---|
383 | *@param os record the properties to this output stream
|
---|
384 | *@param header prepend this header to the property output
|
---|
385 | *@exception IOException on an I/O error during a write.
|
---|
386 | */
|
---|
387 | protected void jdkSaveProperties(Properties props, OutputStream os,
|
---|
388 | String header) throws IOException {
|
---|
389 | try {
|
---|
390 | props.store(os, header);
|
---|
391 |
|
---|
392 | } catch (IOException ioe) {
|
---|
393 | throw new BuildException(ioe, getLocation());
|
---|
394 | } finally {
|
---|
395 | if (os != null) {
|
---|
396 | try {
|
---|
397 | os.close();
|
---|
398 | } catch (IOException ioex) {
|
---|
399 | log("Failed to close output stream");
|
---|
400 | }
|
---|
401 | }
|
---|
402 | }
|
---|
403 | }
|
---|
404 |
|
---|
405 |
|
---|
406 | /**
|
---|
407 | * Uses the DocumentBuilderFactory to get a DocumentBuilder instance.
|
---|
408 | *
|
---|
409 | * @return The DocumentBuilder instance
|
---|
410 | */
|
---|
411 | private static DocumentBuilder getDocumentBuilder() {
|
---|
412 | try {
|
---|
413 | return DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
---|
414 | } catch (Exception e) {
|
---|
415 | throw new ExceptionInInitializerError(e);
|
---|
416 | }
|
---|
417 | }
|
---|
418 | }
|
---|
419 |
|
---|