001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.websvc.jaxrpc.ant;
043:
044: import java.io.File;
045: import java.io.IOException;
046: import java.util.ArrayList;
047: import java.util.Collections;
048: import java.util.Iterator;
049: import java.util.List;
050: import org.apache.tools.ant.BuildException;
051: import org.apache.tools.ant.Task;
052:
053: import org.apache.tools.ant.Project;
054: import org.xml.sax.Attributes;
055: import org.xml.sax.SAXException;
056: import org.xml.sax.helpers.DefaultHandler;
057: import javax.xml.parsers.SAXParser;
058: import javax.xml.parsers.SAXParserFactory;
059: import javax.xml.parsers.ParserConfigurationException;
060:
061: /**
062: * Ant task that calculates wscompile targets and determines if the generated
063: * web service client is up to date or needs to be regenerated.
064: *
065: * @credits Pattern of execute method lifted from ant <uptodate> task.
066: *
067: * @author Peter Williams
068: */
069: public class WsClientUpToDate extends Task {
070:
071: private String _property;
072: private String _value;
073: private File _sourceWsdlFile;
074: private File _sourceConfigFile;
075: private File _targetGenDir;
076: private File _targetConfigFile;
077:
078: private String _targetPackage; // !PW FIXME derive this by parsing config file.
079:
080: /**
081: * The property to set if the calculated output java files and wsdl file are
082: * newer than the source wsdl file AND the target config file is newer than
083: * the source config file.
084: *
085: * @param property the name of the property to set if Targets are up-to-date.
086: */
087: public void setProperty(String property) {
088: _property = property;
089: }
090:
091: /**
092: * The value to set the named property to if the calculated output java files
093: * and wsdl file are newer than the source wsdl file AND the target config
094: * file is newer than the source config file. Defaults to false.
095: *
096: * @param value the value to set the property to if Targets are up-to-date
097: */
098: public void setValue(String value) {
099: _value = value;
100: }
101:
102: /**
103: * Returns the value, or "true" if a specific value wasn't provided.
104: */
105: private String getValue() {
106: return (_value != null) ? _value : "false";
107: }
108:
109: /**
110: * The wsdl file for which we want to confirm the existence of an uptodate
111: * generated client if the property is to be set.
112: *
113: * @param file the wsdl file we are checking against.
114: */
115: public void setSourceWsdl(File file) {
116: _sourceWsdlFile = file;
117: }
118:
119: /**
120: * The wscompile config file for which we want to confirm the existence of
121: * an uptodate generated client if the property is to be set. If this property
122: * is not set, the config file is assumed to be of the form "[source wsdl filename]-config.xml"
123: * and present in the same directory as the source wsdl file.
124: *
125: * @param file the wscompile config file we are checking against.
126: */
127: public void setSourceConfig(File file) {
128: _sourceConfigFile = file;
129: }
130:
131: /**
132: * The target directory that the source to the web service client is
133: * generated into. Actual source files will be in the appropriate package.
134: *
135: * @param file the target directory where the web service client is generated
136: */
137: public void setTargetDir(File file) {
138: _targetGenDir = file;
139: }
140:
141: /**
142: * The target config file. If this property is not specified, the target config
143: * file is assumed to be $targetdir/wsdl/$config-file-name
144: *
145: * @param file the target config file. (Ant applies macro expansion to the source
146: * file to create this in the client generation script.)
147: */
148: public void setTargetConfig(File file) {
149: _targetConfigFile = file;
150: }
151:
152: /**
153: * The target package that the web service client source is generated into.
154: *
155: * !PW FIXME we should parse the config file and obtain this directly.
156: *
157: * @param package the target package that the web service client source is generated into.
158: */
159: public void setTargetPackage(String targetPackage) {
160: _targetPackage = targetPackage;
161: }
162:
163: /**
164: * Evaluate all targets and determine if they are newer than the appropriate
165: * source (and thus uptodate).
166: */
167: public boolean eval() {
168: if (!_sourceWsdlFile.exists()) {
169: throw new BuildException(_sourceWsdlFile.getAbsolutePath()
170: + " not found.");
171: }
172:
173: if (_sourceConfigFile == null) {
174: String cfgNameExt = getBaseName(_sourceWsdlFile)
175: + "-config.xml";
176: _sourceConfigFile = new File(_sourceWsdlFile
177: .getParentFile(), cfgNameExt);
178: }
179:
180: if (!_sourceConfigFile.exists()) {
181: throw new BuildException(_sourceConfigFile
182: .getAbsolutePath()
183: + " not found.");
184: }
185:
186: if (!_targetGenDir.exists()) {
187: log("The target generation directory \""
188: + _targetGenDir.getAbsolutePath()
189: + "\" does not exist.", Project.MSG_VERBOSE);
190: return false;
191: }
192:
193: if (!_targetGenDir.isDirectory()) {
194: throw new BuildException(
195: "property targetdir must be a directory.");
196: }
197:
198: if (_targetConfigFile == null) {
199: _targetConfigFile = new File(_targetGenDir, "wsdl"
200: + File.separator + _sourceConfigFile.getName());
201: }
202:
203: if (!_targetConfigFile.exists()) {
204: log("The target config file \""
205: + _targetConfigFile.getAbsolutePath()
206: + "\" does not exist.", Project.MSG_VERBOSE);
207: return false;
208: }
209:
210: // source wsdl exists. source config exists. target gendir exists. target config exists.
211:
212: // System.out.println("source wsdl: " + _sourceWsdlFile.getPath());
213: // System.out.println("source config: " + _sourceConfigFile.getPath());
214: // System.out.println("target dir: " + _targetGenDir.getPath());
215: // System.out.println("target config: " + _targetConfigFile.getPath());
216:
217: // Compare config files first:
218: if (_targetConfigFile.lastModified() < _sourceConfigFile
219: .lastModified()) {
220: log("Config file \"" + _sourceConfigFile.getAbsolutePath()
221: + " is out of date.", Project.MSG_VERBOSE);
222: return false;
223: }
224:
225: // Now gather data to compare generated client source.
226:
227: // Need to determine target package and convert to a relative directory path.
228: if (_targetPackage == null) {
229: _targetPackage = getClientPackage(_sourceConfigFile);
230: }
231:
232: // System.out.println("target package: " + _targetPackage);
233:
234: // Need to determine target filenames for web service client source code.
235: File targetClientDir = new File(_targetGenDir,
236: convertPackage(_targetPackage));
237: if (!targetClientDir.exists()) {
238: log("The target client source directory \""
239: + targetClientDir.getAbsolutePath()
240: + "\" does not exist.", Project.MSG_VERBOSE);
241: return false;
242: }
243: if (!targetClientDir.isDirectory()) {
244: // !PW Could throw a build exception on this one, but I'll let wscompile
245: // deal with the fallout instead -- it might clean things up or it might fail.
246: log("The target client source directory \""
247: + targetClientDir.getAbsolutePath()
248: + "\" should be a directory.", Project.MSG_VERBOSE);
249: return false;
250: }
251:
252: // System.out.println("target client dir: " + targetClientDir.getPath());
253:
254: List/*File*/clientSources = getClientSources(targetClientDir,
255: _sourceWsdlFile);
256:
257: // validated client source dir, got source file list
258: boolean uptodate = (clientSources.size() > 0) ? true : false;
259: Iterator iter = clientSources.iterator();
260: while (uptodate && iter.hasNext()) {
261: File target = (File) iter.next();
262: if (!target.exists()) {
263: log("Expected target client file \""
264: + target.getAbsolutePath() + "\" not found.",
265: Project.MSG_VERBOSE);
266: uptodate = false;
267: } else {
268: uptodate = uptodate
269: && (target.lastModified() >= _sourceWsdlFile
270: .lastModified());
271: }
272: }
273:
274: return uptodate;
275: }
276:
277: public void execute() throws BuildException {
278: if (_property == null) {
279: throw new BuildException("property attribute is required.",
280: getLocation());
281: }
282: if (_sourceWsdlFile == null) {
283: throw new BuildException(
284: "sourcewsdl attribute is required.", getLocation());
285: }
286: if (_targetGenDir == null) {
287: throw new BuildException(
288: "targetdir attribute is required.", getLocation());
289: }
290: boolean upToDate = eval();
291: if (upToDate) {
292: this .getProject().setNewProperty(_property, getValue());
293: log("Generated client for "
294: + _sourceWsdlFile.getAbsolutePath()
295: + " is up-to-date.", Project.MSG_VERBOSE);
296: }
297: }
298:
299: protected String getBaseName(File f) {
300: String name = f.getName();
301: int i = name.lastIndexOf('.');
302: if (i > -1) {
303: name = name.substring(0, i);
304: }
305: return name;
306: }
307:
308: /** Converts a java package into a relative path
309: */
310: protected String convertPackage(String p) {
311: StringBuffer path = new StringBuffer(p.length());
312: String[] dirs = p.split("\\.");
313: for (int i = 0; i < dirs.length; i++) {
314: if (i > 0) {
315: path.append(File.separator);
316: }
317: path.append(dirs[i]);
318: }
319: return path.toString();
320: }
321:
322: /** Reads the wsdl file and computes a list of expected target files
323: * for the generated web service client.
324: *
325: * Phase 1: Returns list of the service interfaces.
326: *
327: * (We can expand this as necessary to ensure correct results.)
328: */
329: protected List/*File*/getClientSources(File clientDir,
330: File wsdlFile) {
331: ArrayList result = new ArrayList();
332:
333: List serviceNames = getServiceNames(wsdlFile);
334: Iterator iter = serviceNames.iterator();
335: while (iter.hasNext()) {
336: String sn = (String) iter.next();
337: result.add(new File(clientDir, sn + ".java"));
338: }
339:
340: return result;
341: }
342:
343: /** Reads the config file and extracts the target package that the web service
344: * client will be generated into.
345: */
346: protected String getClientPackage(File configFile) {
347: String result = "";
348:
349: try {
350: SAXParserFactory factory = SAXParserFactory.newInstance();
351: factory.setNamespaceAware(true);
352: SAXParser saxParser = factory.newSAXParser();
353: ConfigParser handler = new ConfigParser();
354: saxParser.parse(configFile, handler);
355: result = handler.getPackageName();
356: } catch (ParserConfigurationException ex) {
357: // Bogus config file, return empty package.
358: } catch (SAXException ex) {
359: // Bogus config file, return empty package.
360: } catch (IOException ex) {
361: // Bogus config file, return empty package.
362: }
363:
364: return result;
365: }
366:
367: private List getServiceNames(File wsdlFile) {
368: List result = Collections.EMPTY_LIST;
369:
370: try {
371: SAXParserFactory factory = SAXParserFactory.newInstance();
372: factory.setNamespaceAware(true);
373: SAXParser saxParser = factory.newSAXParser();
374: WsdlParser handler = new WsdlParser();
375: saxParser.parse(wsdlFile, handler);
376: result = handler.getServiceNameList();
377: } catch (ParserConfigurationException ex) {
378: // Bogus WSDL, return empty list.
379: } catch (SAXException ex) {
380: // Bogus WSDL, return empty list.
381: } catch (IOException ex) {
382: // Bogus WSDL, return empty list.
383: }
384:
385: return result;
386: }
387:
388: /* SAX parser to strip the service names from a WSDL file.
389: */
390: private static final class WsdlParser extends DefaultHandler {
391:
392: private static final String W3C_WSDL_SCHEMA = "http://schemas.xmlsoap.org/wsdl";
393: private static final String W3C_WSDL_SCHEMA_SLASH = "http://schemas.xmlsoap.org/wsdl/";
394:
395: private ArrayList serviceNameList;
396:
397: private WsdlParser() {
398: serviceNameList = new ArrayList();
399: }
400:
401: public void startElement(String uri, String localname,
402: String qname, Attributes attributes)
403: throws SAXException {
404: if (W3C_WSDL_SCHEMA.equals(uri)
405: || W3C_WSDL_SCHEMA_SLASH.equals(uri)) {
406: if ("service".equals(localname)) {
407: serviceNameList.add(attributes.getValue("name"));
408: }
409: }
410: }
411:
412: public List/*String*/getServiceNameList() {
413: return serviceNameList;
414: }
415: }
416:
417: /* SAX parser to strip the target package location from a WSCOMPILE config file.
418: */
419: private static final class ConfigParser extends DefaultHandler {
420:
421: private String packageName;
422:
423: private ConfigParser() {
424: packageName = null;
425: }
426:
427: public void startElement(String uri, String localname,
428: String qname, Attributes attributes)
429: throws SAXException {
430: String elementName = localname; // element name
431: if ("".equals(elementName)) {
432: elementName = qname; // not namespace-aware
433: }
434:
435: if ("wsdl".equals(elementName)) {
436: packageName = attributes.getValue("packageName");
437: }
438: }
439:
440: public String getPackageName() {
441: return packageName;
442: }
443: }
444: }
|