001: /*
002: * This file is part of PFIXCORE.
003: *
004: * PFIXCORE is free software; you can redistribute it and/or modify
005: * it under the terms of the GNU Lesser General Public License as published by
006: * the Free Software Foundation; either version 2 of the License, or
007: * (at your option) any later version.
008: *
009: * PFIXCORE is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public License
015: * along with PFIXCORE; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: */
019: package de.schlund.pfixcore.webservice.jaxws.generate;
020:
021: import java.io.File;
022: import java.io.FileOutputStream;
023: import java.lang.reflect.Method;
024: import java.util.HashSet;
025: import java.util.Set;
026:
027: import javax.xml.parsers.DocumentBuilder;
028: import javax.xml.parsers.DocumentBuilderFactory;
029: import javax.xml.transform.OutputKeys;
030: import javax.xml.transform.Transformer;
031: import javax.xml.transform.TransformerFactory;
032: import javax.xml.transform.dom.DOMSource;
033: import javax.xml.transform.stream.StreamResult;
034:
035: import org.apache.tools.ant.BuildException;
036: import org.apache.tools.ant.Project;
037: import org.apache.tools.ant.Task;
038: import org.apache.tools.ant.types.Path;
039: import org.apache.tools.ant.types.Reference;
040: import org.w3c.dom.Document;
041: import org.w3c.dom.Element;
042: import org.w3c.dom.NodeList;
043:
044: import com.sun.tools.ws.ant.WsGen;
045:
046: import de.schlund.pfixcore.webservice.Constants;
047: import de.schlund.pfixcore.webservice.config.Configuration;
048: import de.schlund.pfixcore.webservice.config.ConfigurationReader;
049: import de.schlund.pfixcore.webservice.config.GlobalServiceConfig;
050: import de.schlund.pfixcore.webservice.config.ServiceConfig;
051: import de.schlund.pfixxml.config.GlobalConfigurator;
052: import de.schlund.pfixxml.resources.FileResource;
053: import de.schlund.pfixxml.resources.ResourceUtil;
054: import de.schlund.pfixxml.util.FileUtils;
055:
056: /**
057: * Ant task which iterates over the webservice configurations of all projects
058: * and creates WSDL descriptions and Javascript stubs for the contained SOAP
059: * services. Additionally the endpoint configuration files for the JAXWS runtime
060: * are created.
061: *
062: * @author mleidig@schlund.de
063: */
064: public class WebserviceTask extends Task {
065:
066: private final static String XMLNS_JAXWS_RUNTIME = "http://java.sun.com/xml/ns/jax-ws/ri/runtime";
067: private final static String XMLNS_JAVAEE = "http://java.sun.com/xml/ns/javaee";
068:
069: private File prjfile;
070: private File prjdir;
071: private File builddir;
072: private File tmpdir;
073: private File webappsdir;
074: private Path classPath;
075: private boolean standalone;
076: private int portbase;
077:
078: private Set<File> wsgenDirs;
079:
080: /**
081: * Iterate over projects and webservice configurations and generate WSDL
082: * descriptions, Javascript stubs and endpoint configurations.
083: */
084: @Override
085: public void execute() throws BuildException {
086: try {
087: GlobalConfigurator.setDocroot(prjdir.getAbsolutePath());
088: } catch (IllegalStateException e) {
089: // Ignore exception as there is no problem
090: // if the docroot has already been configured
091: }
092: try {
093: wsgenDirs = new HashSet<File>();
094: Document doc = TaskUtils.loadDoc(prjfile);
095: NodeList nl = doc.getElementsByTagName("project");
096: // iterate over projects
097: for (int i = 0; i < nl.getLength(); i++) {
098: Element elem = (Element) nl.item(i);
099: String prjName = elem.getAttribute("name");
100: FileResource wsConfFile = ResourceUtil
101: .getFileResource("file://"
102: + prjdir.getAbsolutePath() + "/"
103: + prjName + "/" + "conf" + "/"
104: + "webservice.conf.xml");
105: // go on processing if webservices found
106: if (wsConfFile.exists()) {
107: int wsdlCount = 0;
108: int stubCount = 0;
109: File tmpDir = getTmpDir(prjName);
110: // read webservice configuration
111: Configuration srvConf = ConfigurationReader
112: .read(wsConfFile);
113: GlobalServiceConfig globConf = srvConf
114: .getGlobalServiceConfig();
115: Configuration refSrvConf = null;
116: GlobalServiceConfig refGlobConf = null;
117: boolean globalConfChanged = false;
118: // read last built webservice configuration
119: FileResource refWsConfFile = ResourceUtil
120: .getFileResource("file://"
121: + tmpDir.getAbsolutePath() + "/"
122: + "webservice.conf.ser");
123: if (refWsConfFile.exists()) {
124: try {
125: refSrvConf = ConfigurationReader
126: .deserialize(refWsConfFile);
127: refGlobConf = refSrvConf
128: .getGlobalServiceConfig();
129: if (!globConf.equals(refGlobConf))
130: globalConfChanged = true;
131: } catch (Exception x) {
132: log(
133: "Error deserializing old reference configuration",
134: Project.MSG_VERBOSE);
135: log(
136: "Warning: Ignore old reference configuration because it can't be deserialized. "
137: + "Services will be built from scratch.",
138: Project.MSG_WARN);
139: }
140: }
141: // Setup WSDL repository
142: File appDir = new File(webappsdir, prjName);
143: if (!appDir.exists())
144: throw new BuildException(
145: "Web application directory of project '"
146: + prjName + "' doesn't exist");
147: File wsdlDir = tmpDir;
148: if (globConf.getWSDLSupportEnabled()) {
149: String wsdlRepo = globConf.getWSDLRepository();
150: if (wsdlRepo.startsWith("/"))
151: wsdlRepo.substring(1);
152: wsdlDir = new File(appDir, wsdlRepo);
153: if (!wsdlDir.exists()) {
154: boolean ok = wsdlDir.mkdir();
155: if (!ok)
156: throw new BuildException(
157: "Can't create WSDL directory "
158: + wsdlDir
159: .getAbsolutePath());
160: }
161: }
162: // Setup javascript stub repository
163: File stubDir = tmpDir;
164: if (globConf.getStubGenerationEnabled()) {
165: String stubRepo = globConf.getStubRepository();
166: if (stubRepo.startsWith("/"))
167: stubRepo.substring(1);
168: stubDir = new File(appDir, stubRepo);
169: if (!stubDir.exists()) {
170: boolean ok = stubDir.mkdir();
171: if (!ok)
172: throw new BuildException(
173: "Can't create webservice stub directory "
174: + stubDir
175: .getAbsolutePath());
176: }
177: }
178: // Check if WEB-INF exists
179: File webInfDir = new File(appDir, "WEB-INF");
180: if (!webInfDir.exists())
181: throw new BuildException(
182: "Web application WEB-INF subdirectory of project '"
183: + prjName + "' doesn't exist");
184: // Setup endpoint document
185: DocumentBuilderFactory dbf = DocumentBuilderFactory
186: .newInstance();
187: DocumentBuilder db = dbf.newDocumentBuilder();
188: Document endPointsDoc = db.newDocument();
189: Element endPointsElem = endPointsDoc
190: .createElementNS(XMLNS_JAXWS_RUNTIME,
191: "ws:endpoints");
192: endPointsElem.setAttribute("version", "2.0");
193: endPointsDoc.appendChild(endPointsElem);
194: boolean hasSOAPService = false;
195: // Iterate over services
196: for (ServiceConfig conf : srvConf
197: .getServiceConfig()) {
198: if (conf.getProtocolType().equals(
199: Constants.PROTOCOL_TYPE_ANY)
200: || conf.getProtocolType().equals(
201: Constants.PROTOCOL_TYPE_SOAP)) {
202: hasSOAPService = true;
203: ServiceConfig refConf = null;
204: if (refSrvConf != null)
205: refConf = refSrvConf
206: .getServiceConfig(conf
207: .getName());
208: File wsdlFile = new File(wsdlDir, conf
209: .getName()
210: + ".wsdl");
211: // Add endpoint configuration
212: Element endPointElem = endPointsDoc
213: .createElementNS(
214: XMLNS_JAXWS_RUNTIME,
215: "ws:endpoint");
216: endPointsElem.appendChild(endPointElem);
217: endPointElem.setAttribute("name", conf
218: .getName());
219: endPointElem.setAttribute("implementation",
220: conf.getImplementationName()
221: + "JAXWS");
222: endPointElem.setAttribute("url-pattern",
223: globConf.getRequestPath() + "/"
224: + conf.getName());
225: Element chainsElem = endPointsDoc
226: .createElementNS(XMLNS_JAVAEE,
227: "ee:handler-chains");
228: Element chainElem = endPointsDoc
229: .createElementNS(XMLNS_JAVAEE,
230: "ee:handler-chain");
231: chainsElem.appendChild(chainElem);
232: Element handlerElem = endPointsDoc
233: .createElementNS(XMLNS_JAVAEE,
234: "ee:handler");
235: chainElem.appendChild(handlerElem);
236: Element handlerClassElem = endPointsDoc
237: .createElementNS(XMLNS_JAVAEE,
238: "ee:handler-class");
239: handlerElem.appendChild(handlerClassElem);
240: handlerClassElem
241: .setTextContent("de.schlund.pfixcore.webservice.jaxws.ErrorHandler");
242: if (globConf.getMonitoringEnabled()
243: || globConf.getLoggingEnabled()) {
244: handlerElem = endPointsDoc
245: .createElementNS(XMLNS_JAVAEE,
246: "ee:handler");
247: chainElem.appendChild(handlerElem);
248: handlerClassElem = endPointsDoc
249: .createElementNS(XMLNS_JAVAEE,
250: "ee:handler-class");
251: handlerElem
252: .appendChild(handlerClassElem);
253: handlerClassElem
254: .setTextContent("de.schlund.pfixcore.webservice.jaxws.RecordingHandler");
255: }
256: endPointElem.appendChild(chainsElem);
257: // Check if WSDL/Javascript has to be built
258: if (refConf == null
259: || !wsdlFile.exists()
260: || globalConfChanged
261: || !conf.equals(refConf)
262: || TaskUtils.checkInterfaceChange(
263: conf.getInterfaceName(),
264: builddir, wsdlFile)) {
265: checkInterface(conf.getInterfaceName());
266: Class<?> implClass = Class.forName(conf
267: .getImplementationName());
268: // Generate WSDL
269: File wsgenDir = new File(
270: tmpdir,
271: "wsdl/"
272: + conf.getName()
273: + "/"
274: + conf
275: .getImplementationName());
276: if (!wsgenDirs.contains(wsgenDir)) {
277: if (!wsgenDir.exists())
278: wsgenDir.mkdirs();
279: WsGen wsgen = new WsGen();
280: wsgen.setProject(getProject());
281: wsgen.setDynamicAttribute("keep",
282: "true");
283: wsgen.setDynamicAttribute(
284: "sourcedestdir", "gensrc");
285: wsgen.setDynamicAttribute(
286: "genwsdl", "true");
287: wsgen.setDynamicAttribute(
288: "destdir", "build");
289: wsgen.setDynamicAttribute(
290: "resourcedestdir", wsgenDir
291: .getAbsolutePath());
292: wsgen.setDynamicAttribute(
293: "classpath", classPath
294: .toString());
295: wsgen
296: .setDynamicAttribute(
297: "sei",
298: conf
299: .getImplementationName()
300: + "JAXWS");
301: String serviceName = "{"
302: + TaskUtils
303: .getTargetNamespace(implClass)
304: + "}" + conf.getName();
305: wsgen.setDynamicAttribute(
306: "servicename", serviceName);
307: wsgen.execute();
308: wsgenDirs.add(wsgenDir);
309: }
310: FileUtils.copyFiles(wsgenDir, wsdlDir,
311: ".*wsdl", ".*xsd");
312: // Replace endpoint URL
313: Element srvElem = (Element) elem
314: .getElementsByTagName(
315: "servername").item(0);
316: if (srvElem == null)
317: throw new BuildException(
318: "Missing servername element in configuration of project '"
319: + prjName + "'");
320: String srvName = srvElem
321: .getTextContent().trim();
322: String srvPort = "";
323: if (standalone)
324: srvPort = ":" + (portbase + 80);
325: String wsUrl = "http://" + srvName
326: + srvPort
327: + globConf.getRequestPath()
328: + "/" + conf.getName();
329: FileUtils.searchAndReplace(wsdlFile,
330: "UTF-8",
331: "REPLACE_WITH_ACTUAL_URL",
332: wsUrl);
333: wsdlCount++;
334: // Generate javascript stubs
335: if (globConf.getStubGenerationEnabled()) {
336: File stubFile = new File(stubDir,
337: conf.getName() + ".js");
338: if (!stubFile.exists()
339: || stubFile.lastModified() < wsdlFile
340: .lastModified()) {
341: Wsdl2Js task = new Wsdl2Js();
342: task.setInputFile(wsdlFile);
343: task.setOutputFile(stubFile);
344: task.generate();
345: stubCount++;
346: }
347: }
348: }
349: }
350: }
351: if (wsdlCount > 0)
352: log("Generated " + wsdlCount + " WSDL file"
353: + (wsdlCount == 1 ? "" : "s") + ".");
354: if (stubCount > 0)
355: log("Generated " + stubCount
356: + " Javascript stub file"
357: + (stubCount == 1 ? "" : "s") + ".");
358: // Generate JAXWS runtime endpoint configuration
359: File endPointsFile = new File(webInfDir,
360: "sun-jaxws.xml");
361: if (hasSOAPService
362: && (!endPointsFile.exists() || wsdlCount > 0)) {
363: Transformer t = TransformerFactory
364: .newInstance().newTransformer();
365: t.setOutputProperty(OutputKeys.INDENT, "yes");
366: t.transform(new DOMSource(endPointsDoc),
367: new StreamResult(new FileOutputStream(
368: endPointsFile)));
369: log("Generated JAXWS runtime endpoint configuration.");
370: }
371: // Store current webservice configuration file
372: ConfigurationReader.serialize(srvConf,
373: refWsConfFile);
374: }
375: }
376: } catch (Exception x) {
377: throw new BuildException(x);
378: }
379: }
380:
381: /**
382: * Get or create temporary/webservice artifact directory for a specific
383: * project.
384: */
385: private File getTmpDir(String project) throws BuildException {
386: if (!tmpdir.exists()) {
387: boolean ok = tmpdir.mkdir();
388: if (!ok)
389: throw new BuildException(
390: "Can't create temporary directory "
391: + tmpdir.getAbsolutePath());
392: }
393: File dir = new File(tmpdir, project);
394: if (!dir.exists()) {
395: boolean ok = dir.mkdir();
396: if (!ok)
397: throw new BuildException(
398: "Can't create temporary directory "
399: + dir.getAbsolutePath());
400: }
401: return dir;
402: }
403:
404: /**
405: * Check if class is a legal webservice interface (being an interface and
406: * having no overloaded methods)
407: */
408: private void checkInterface(String className) throws BuildException {
409: try {
410: Class<?> clazz = Class.forName(className);
411: if (!clazz.isInterface())
412: throw new BuildException(
413: "Web service interface class doesn't represent an interface type");
414: Method[] methods = clazz.getDeclaredMethods();
415: HashSet<String> names = new HashSet<String>();
416: for (int i = 0; i < methods.length; i++) {
417: String name = methods[i].getName();
418: if (names.contains(name))
419: throw new BuildException(
420: "Web service interface class '"
421: + className
422: + "' contains "
423: + "overloaded method '"
424: + name
425: + "'. Method overloading isn't allowed in web service interface definitions, "
426: + "as future WSDL versions (1.2+) will no longer support operation overloading.");
427: names.add(name);
428: }
429: } catch (ClassNotFoundException x) {
430: throw new BuildException("Web service interface class "
431: + className + " not found", x);
432: }
433: }
434:
435: // Ant-task properties:
436:
437: /**
438: * Set project configuration file.
439: */
440: public void setPrjfile(File prjfile) {
441: this .prjfile = prjfile;
442: }
443:
444: /**
445: * Set project directory.
446: */
447: public void setPrjdir(File prjdir) {
448: this .prjdir = prjdir;
449: }
450:
451: /**
452: * Set build directory.
453: */
454: public void setBuildDir(File builddir) {
455: this .builddir = builddir;
456: }
457:
458: /**
459: * Set directory for temporary/webservice build artifacts.
460: */
461: public void setTmpdir(File tmpdir) {
462: this .tmpdir = tmpdir;
463: }
464:
465: /**
466: * Set webapp deployment directory.
467: */
468: public void setWebappsdir(File webappsdir) {
469: this .webappsdir = webappsdir;
470: }
471:
472: /**
473: * Set reference to classpath containing application classes.
474: */
475: public void setClasspathRef(Reference ref) {
476: classPath = new Path(getProject());
477: classPath.createPath().setRefid(ref);
478: }
479:
480: /**
481: * Set if it's build in Tomcat standalone mode.
482: */
483: public void setStandalone(boolean standalone) {
484: this .standalone = standalone;
485: }
486:
487: /**
488: * Set the relative port for the Tomcat.
489: */
490: public void setPortbase(int portbase) {
491: this.portbase = portbase;
492: }
493:
494: }
|