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: package org.apache.tools.ant.taskdefs.optional.dotnet;
019:
020: import java.io.File;
021: import java.util.Vector;
022: import java.util.Iterator;
023: import java.net.MalformedURLException;
024:
025: import org.apache.tools.ant.BuildException;
026: import org.apache.tools.ant.Project;
027: import org.apache.tools.ant.Task;
028: import org.apache.tools.ant.types.EnumeratedAttribute;
029: import org.apache.tools.ant.taskdefs.condition.Os;
030: import org.apache.tools.ant.util.FileUtils;
031:
032: /**
033: * Converts a WSDL file or URL resource into a .NET language.
034: *
035: * Why add a wrapper to the MS WSDL tool?
036: * So that you can verify that your web services, be they written with Axis or
037: *anyone else's SOAP toolkit, work with .NET clients.
038: *
039: *This task is dependency aware when using a file as a source and destination;
040: *so if you <get> the file (with <code>usetimestamp="true"</code>) then
041: *you only rebuild stuff when the WSDL file is changed. Of course,
042: *if the server generates a new timestamp every time you ask for the WSDL,
043: *this is not enough...use the <filesmatch> <condition> to
044: *to byte for byte comparison against a cached WSDL file then make
045: *the target conditional on that test failing.
046:
047: * See "Creating an XML Web Service Proxy", "wsdl.exe" docs in
048: * the framework SDK documentation
049: * @version 0.5
050: * @ant.task category="dotnet"
051: * @since Ant 1.5
052: */
053:
054: public class WsdlToDotnet extends Task {
055:
056: /**
057: * used for timestamp checking
058: */
059: private static final FileUtils FILE_UTILS = FileUtils
060: .getFileUtils();
061:
062: /**
063: * name of output file (required)
064: */
065: private File destFile = null;
066:
067: /**
068: * language; defaults to C#
069: */
070: private String language = "CS";
071:
072: /**
073: * flag set to true to generate server side skeleton
074: */
075: private boolean server = false;
076:
077: /**
078: * namespace
079: */
080: private String namespace = null;
081:
082: /**
083: * flag to control action on execution trouble
084: */
085: private boolean failOnError = true;
086:
087: // CheckStyle:VisibilityModifier OFF - bc
088: /**
089: * any extra command options?
090: */
091: protected String extraOptions = null;
092:
093: // CheckStyle:VisibilityModifier ON
094:
095: /**
096: * protocol string. Exact value set depends on SOAP stack version.
097: * @since Ant 1.7
098: */
099: private String protocol = null;
100:
101: /**
102: * should errors come in an IDE format. This
103: * is WSE only.
104: * @since Ant 1.7
105: */
106: private boolean ideErrors = false;
107:
108: /**
109: * filesets of file to compile
110: * @since Ant 1.7
111: */
112: private Vector schemas = new Vector();
113:
114: /**
115: * our WSDL file.
116: * @since ant1.7
117: */
118: private Schema wsdl = new Schema();
119:
120: /**
121: * compiler
122: * @since ant1.7
123: */
124: private Compiler compiler = null;
125:
126: /**
127: * error message: dest file is a directory
128: */
129: public static final String ERROR_DEST_FILE_IS_DIR = "destination file is a directory";
130:
131: /**
132: * error message: no dest file
133: */
134: public static final String ERROR_NO_DEST_FILE = "destination file must be specified";
135:
136: /**
137: * Name of the file to generate. Required
138: * @param destFile filename
139: */
140: public void setDestFile(File destFile) {
141: this .destFile = destFile;
142: }
143:
144: /**
145: * Sets the URL to fetch. Fetching is by wsdl.exe; Ant proxy settings
146: * are ignored; either url or srcFile is required.
147: * @param url url to save
148: */
149:
150: public void setUrl(String url) {
151: wsdl.setUrl(url);
152: }
153:
154: /**
155: * The local WSDL file to parse; either url or srcFile is required.
156: * @param srcFile WSDL file
157: */
158: public void setSrcFile(File srcFile) {
159: wsdl.setFile(srcFile);
160: }
161:
162: /**
163: * set the language; one of "CS", "JS", or "VB"
164: * optional, default is CS for C# source
165: * @param language language to generate
166: */
167: public void setLanguage(String language) {
168: this .language = language;
169: }
170:
171: /**
172: * flag to enable server side code generation;
173: * optional, default=false
174: * @param server server-side flag
175: */
176:
177: public void setServer(boolean server) {
178: this .server = server;
179: }
180:
181: /**
182: * namespace to place the source in.
183: * optional; default ""
184: * @param namespace new namespace
185: */
186: public void setNamespace(String namespace) {
187: this .namespace = namespace;
188: }
189:
190: /**
191: * Whether or not a failure should halt the build.
192: * Optional - default is <code>true</code>.
193: * @param failOnError new failure option
194: */
195: public void setFailOnError(boolean failOnError) {
196: this .failOnError = failOnError;
197: }
198:
199: /**
200: * Any extra WSDL.EXE options which aren't explicitly
201: * supported by the ant wrapper task; optional
202: *
203: *@param extraOptions The new ExtraOptions value
204: */
205: public void setExtraOptions(String extraOptions) {
206: this .extraOptions = extraOptions;
207: }
208:
209: /**
210: * Defines wether errors are machine parseable.
211: * Optional, default=true
212: *
213: * @since Ant 1.7
214: * @param ideErrors a <code>boolean</code> value.
215: */
216: public void setIdeErrors(boolean ideErrors) {
217: this .ideErrors = ideErrors;
218: }
219:
220: /**
221: * what protocol to use. SOAP, SOAP1.2, HttpPost and HttpGet
222: * are the base options. Different version and implementations may.
223: * offer different options.
224: * @since Ant 1.7
225: *
226: * @param protocol the protocol to use.
227: */
228: public void setProtocol(String protocol) {
229: this .protocol = protocol;
230: }
231:
232: /**
233: * add a new source schema to the compilation
234: * @since Ant 1.7
235: *
236: * @param source a nested schema element.
237: */
238: public void addSchema(Schema source) {
239: schemas.add(source);
240: }
241:
242: /**
243: * flag to trigger turning a filename into a file:url
244: * ignored for the mono compiler.
245: * @param b a <code>boolean</code> value.
246: */
247: public void setMakeURL(boolean b) {
248: wsdl.setMakeURL(b);
249: }
250:
251: /**
252: * identify the compiler
253: * @since Ant 1.7
254: * @param compiler the enumerated value.
255: */
256: public void setCompiler(Compiler compiler) {
257: this .compiler = compiler;
258: }
259:
260: /**
261: * validation code
262: * @throws BuildException if validation failed
263: */
264: protected void validate() throws BuildException {
265: if (destFile == null) {
266: throw new BuildException(ERROR_NO_DEST_FILE);
267: }
268: if (destFile.isDirectory()) {
269: throw new BuildException(ERROR_DEST_FILE_IS_DIR);
270: }
271: wsdl.validate();
272: }
273:
274: /**
275: * do the work by building the command line and then calling it
276: *
277: *@throws BuildException if validation or execution failed
278: */
279: public void execute() throws BuildException {
280: log(
281: "This task is deprecated and will be removed in a future version\n"
282: + "of Ant. It is now part of the .NET Antlib:\n"
283: + "http://ant.apache.org/antlibs/dotnet/index.html",
284: Project.MSG_WARN);
285:
286: if (compiler == null) {
287: compiler = Compiler.createDefaultCompiler();
288: }
289: validate();
290: NetCommand command = new NetCommand(this , "WSDL", compiler
291: .getCommand());
292: command.setFailOnError(failOnError);
293: //fill in args
294: compiler.applyExtraArgs(command);
295: command.addArgument("/nologo");
296: command.addArgument("/out:" + destFile);
297: command.addArgument("/language:", language);
298: if (server) {
299: command.addArgument("/server");
300: }
301: command.addArgument("/namespace:", namespace);
302: if (protocol != null) {
303: command.addArgument("/protocol:" + protocol);
304: }
305: if (ideErrors) {
306: command.addArgument("/parsableErrors");
307: }
308: command.addArgument(extraOptions);
309:
310: //set source and rebuild options
311: boolean rebuild = true;
312: long destLastModified = -1;
313:
314: //rebuild unless the dest file is newer than the source file
315: if (destFile.exists()) {
316: destLastModified = destFile.lastModified();
317: rebuild = isRebuildNeeded(wsdl, destLastModified);
318: }
319: String path;
320: //mark for a rebuild if the dest file is newer
321: path = wsdl.evaluate();
322: if (!compiler.supportsAbsoluteFiles() && wsdl.getFile() != null) {
323: // Mono 1.0's wsdl doesn't deal with absolute paths
324: File f = wsdl.getFile();
325: command.setDirectory(f.getParentFile());
326: path = f.getName();
327: }
328: command.addArgument(path);
329: //add in any extra files.
330: //this is an error in mono, but we do not warn on it as they may fix that outside
331: //the ant build cycle.
332: Iterator it = schemas.iterator();
333: while (it.hasNext()) {
334: Schema schema = (Schema) it.next();
335: //mark for a rebuild if we are newer
336: rebuild |= isRebuildNeeded(schema, destLastModified);
337: command.addArgument(schema.evaluate());
338: }
339: //conditionally compile
340: if (rebuild) {
341: command.runCommand();
342: }
343: }
344:
345: /**
346: * checks for a schema being out of data
347: * @param schema url/file
348: * @param destLastModified timestamp, -1 for no dest
349: * @return true if a rebuild is needed.
350: */
351: private boolean isRebuildNeeded(Schema schema, long destLastModified) {
352: if (destLastModified == -1) {
353: return true;
354: }
355: return !FILE_UTILS.isUpToDate(schema.getTimestamp(),
356: destLastModified);
357: }
358:
359: /**
360: * nested schema class
361: * Only supported on NET until mono add multi-URL handling on the command line
362: */
363: public static class Schema {
364: private File file;
365: private String url;
366: private boolean makeURL = false;
367:
368: // Errors
369: /** One of file or url must be set */
370: public static final String ERROR_NONE_DECLARED = "One of file and url must be set";
371: /** Only one of file or url */
372: public static final String ERROR_BOTH_DECLARED = "Only one of file or url can be set";
373: /** Not found */
374: public static final String ERROR_FILE_NOT_FOUND = "Not found: ";
375: /** File is a directory */
376: public static final String ERROR_FILE_IS_DIR = "File is a directory: ";
377: /** Could not URL convert */
378: public static final String ERROR_NO_URL_CONVERT = "Could not URL convert ";
379:
380: /**
381: * validate the schema
382: */
383: public void validate() {
384:
385: if (file != null) {
386: if (!file.exists()) {
387: throw new BuildException(ERROR_FILE_NOT_FOUND
388: + file.toString());
389: }
390: if (file.isDirectory()) {
391: throw new BuildException(ERROR_FILE_IS_DIR
392: + file.toString());
393: }
394: }
395: if (file != null && url != null) {
396: throw new BuildException(ERROR_BOTH_DECLARED);
397: }
398: if (file == null && url == null) {
399: throw new BuildException(ERROR_NONE_DECLARED);
400: }
401: }
402:
403: /**
404: * Validate our settings.
405: * @return either the URL or the full file path
406: */
407: public String evaluate() {
408: validate();
409: if (url != null) {
410: return getUrl();
411: }
412: if (makeURL) {
413: try {
414: return file.toURL().toExternalForm();
415: } catch (MalformedURLException e) {
416: throw new BuildException(ERROR_NO_URL_CONVERT
417: + file);
418: }
419: }
420: return file.toString();
421: }
422:
423: /**
424: * Get the file.
425: * @return the file used.
426: */
427: public File getFile() {
428: return file;
429: }
430:
431: /**
432: * name of a file to use as a source of WSDL or XSD data
433: * @param file the file to use.
434: */
435: public void setFile(File file) {
436: this .file = file;
437: }
438:
439: /**
440: * Get the url.
441: * @return the URL of the resource.
442: */
443: public String getUrl() {
444: return url;
445: }
446:
447: /**
448: * url of a resource.
449: * URLs have no timestamp checking, and are not validated
450: * @param url the URL string to use.
451: */
452: public void setUrl(String url) {
453: this .url = url;
454: }
455:
456: /**
457: * Get the makeURL attribute.
458: * @return the attribute.
459: */
460: public boolean isMakeURL() {
461: return makeURL;
462: }
463:
464: /**
465: * flag to request that a file is turned into an absolute file: URL
466: * before being passed to the WSDL compiler
467: * @param makeURL a <code>boolean</code> value.
468: */
469: public void setMakeURL(boolean makeURL) {
470: this .makeURL = makeURL;
471: }
472:
473: /**
474: * Gets the file timestamp.
475: * @return the timestamp of a file, or -1 for a URL (meaning we do not know its age)
476: */
477: public long getTimestamp() {
478: if (file != null) {
479: return file.lastModified();
480: } else {
481: return -1;
482: }
483: }
484: }
485:
486: /**
487: * The enumerated values for our compiler
488: */
489: public static class Compiler extends EnumeratedAttribute {
490:
491: /** microsoft */
492: public static final String COMPILER_MS = "microsoft";
493: /** mono */
494: public static final String COMPILER_MONO = "mono";
495: /** microsoft-on-mono */
496: public static final String COMPILER_MS_ON_MONO = "microsoft-on-mono";
497: // CheckStyle:VisibilityModifier OFF - bc
498: /** the index to string mappings */
499: String[] compilers = { COMPILER_MS, COMPILER_MONO,
500: COMPILER_MS_ON_MONO };
501:
502: /** WSDL */
503: public static final String EXE_WSDL = "wsdl";
504: /** MONO */
505: public static final String EXE_MONO = "mono";
506: /**
507: * programs to run
508: */
509: String[] compilerExecutables = { EXE_WSDL, EXE_WSDL, EXE_MONO };
510:
511: /**
512: * extra things
513: */
514: String[][] extraCompilerArgs = { {}, {}, { EXE_WSDL + ".exe" } };
515:
516: boolean[] absoluteFiles = { true, false, true };
517:
518: // CheckStyle:VisibilityModifier ON
519: /**
520: * This is the only method a subclass needs to implement.
521: *
522: * @return an array holding all possible values of the enumeration.
523: * The order of elements must be fixed so that <tt>indexOfValue(String)</tt>
524: * always return the same index for the same value.
525: */
526: public String[] getValues() {
527: return compilers;
528: }
529:
530: /**
531: * Create the default compiler for this platform.
532: * @return the default compiler
533: */
534: public static Compiler createDefaultCompiler() {
535: Compiler c = new Compiler();
536: String compilerName;
537: compilerName = Os.isFamily("windows") ? COMPILER_MS
538: : COMPILER_MONO;
539: c.setValue(compilerName);
540: return c;
541: }
542:
543: /**
544: * return the command to run
545: * @return the command
546: */
547: public String getCommand() {
548: return compilerExecutables[getIndex()];
549: }
550:
551: /**
552: * return any extra arguments for the compiler
553: * @return extra compiler arguments
554: */
555: public String[] getExtraArgs() {
556: return extraCompilerArgs[getIndex()];
557: }
558:
559: /**
560: * Get where the current value supports absolute files.
561: * @return true if the compiler does supports absolute files.
562: */
563: public boolean supportsAbsoluteFiles() {
564: return absoluteFiles[getIndex()];
565: }
566:
567: /**
568: * apply any extra arguments of this class
569: * @param command the command to apply the arguments to.
570: */
571: public void applyExtraArgs(NetCommand command) {
572: String[] args = getExtraArgs();
573: for (int i = 0; i < args.length; i++) {
574: command.addArgument(args[i]);
575: }
576: }
577:
578: }
579:
580: }
|