001: /*
002: * (C) Copyright 2000 - 2005 Nabh Information Systems, Inc.
003: *
004: * This program is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU General Public License
006: * as published by the Free Software Foundation; either version 2
007: * of the License, or (at your option) any later version.
008: *
009: * This program 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 General Public License for more details.
013: *
014: * You should have received a copy of the GNU General Public License
015: * along with this program; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
017: *
018: */
019: package com.nabhinc.ws.server;
020:
021: import java.io.File;
022: import java.io.FileInputStream;
023: import java.io.FileNotFoundException;
024: import java.io.FileOutputStream;
025: import java.io.IOException;
026: import java.io.InputStream;
027: import java.io.OutputStreamWriter;
028: import java.io.PrintWriter;
029: import java.net.HttpURLConnection;
030:
031: import javax.servlet.ServletConfig;
032: import javax.servlet.ServletException;
033: import javax.servlet.http.HttpServletRequest;
034: import javax.servlet.http.HttpServletResponse;
035: import javax.xml.bind.JAXBException;
036: import javax.xml.parsers.DocumentBuilder;
037: import javax.xml.parsers.DocumentBuilderFactory;
038:
039: import org.apache.axis.utils.XMLUtils;
040: import org.w3c.dom.Document;
041: import org.w3c.dom.Element;
042:
043: import com.nabhinc.core.AbstractServlet;
044: import com.nabhinc.util.IOUtil;
045: import com.nabhinc.util.StringUtil;
046: import com.nabhinc.util.XMLUtil;
047: import com.nabhinc.util.db.DBConfigUtil;
048: import com.nabhinc.ws.core.PropertyInfo;
049: import com.nabhinc.ws.core.WebServiceException;
050: import com.nabhinc.ws.core.WebServiceUtil;
051: import com.nabhinc.ws.spi.UserManager;
052:
053: /**
054: * <code>WebServiceServlet</code> is the controller for Stringbeans Web services
055: * framework. It fields requests for service WSDL, service listing, and service
056: * operation invocations. The first two requests are sent as "GET" requests. All
057: * "POST" requests are assumed to be for invoking operations on hosted Web
058: * services.
059: *
060: * @author Padmanabh Dabke
061: * (c) 2005 Nabh Information Systems, Inc. All Rights Reserved.
062: */
063: public class WebServiceServlet extends AbstractServlet {
064: /**
065: * <code>serialVersionUID</code>
066: */
067: private static final long serialVersionUID = 3257567312831068469L;
068:
069: // Singleton instance
070: private static WebServiceServlet wssSelf = null;
071:
072: // Server context that provides server URLs, server ID, etc.
073: private HTTPWebServiceContext wssServerContext = null;
074:
075: // Flag indicating if the server is being run in development mode
076: private boolean wssDevelopmentMode = false;
077: // Helper objects
078: private WebServiceDirectory wssServiceDirectory = null;
079: private ServletRequestProcessor wssRequestProcessor = null;
080: private UserManager wssUserManager = null;
081:
082: // Cached configuration properties for system objects.
083: private PropertyInfo[] wssUserManagerProps = null;
084: private PropertyInfo[] wssServiceDirectoryProps = null;
085: private PropertyInfo[] wssRequestProcessorProps = null;
086:
087: public static final String ROOT_DIR_PARAM_NAME = "rootDir";
088: private String wssConfigFilePath = null;
089:
090: public void init(ServletConfig config) throws ServletException {
091: super .init(config);
092:
093: // Set singleton. Must set singleton here since WebServiceManager uses
094: // logging methods.
095: wssSelf = this ;
096:
097: // Load database configuration if available
098: InputStream is = null;
099: try {
100: // String dbConfigFile = config.getServletContext().getRealPath("/WEB-INF/dbconfig.xml");
101: is = config.getServletContext().getResourceAsStream(
102: "/WEB-INF/dbconfig.xml");
103: DBConfigUtil.init(is, config.getServletContext()
104: .getRealPath(""));
105: } catch (JAXBException ex) {
106: throw new ServletException(
107: "Database configuration file not found.", ex);
108: } catch (IOException ex) {
109: throw new ServletException(
110: "IO exception in initializing database configuration.",
111: ex);
112:
113: } finally {
114: try {
115: if (is != null)
116: is.close();
117: } catch (Exception e) {
118: }
119: }
120:
121: String relPath = "/WEB-INF/webservice.xml";
122: wssConfigFilePath = config.getServletContext().getRealPath(
123: relPath);
124: String rootDir = config.getInitParameter(ROOT_DIR_PARAM_NAME);
125: is = null;
126:
127: // Check for webservice.xml file
128: if (rootDir == null) {
129: is = config.getServletContext()
130: .getResourceAsStream(relPath);
131: } else {
132: String absPath = rootDir + File.separator + "WEB-INF"
133: + File.separator + "webservice.xml";
134: File f = new File(absPath);
135: if (f.exists()) {
136: try {
137: wssConfigFilePath = absPath;
138: is = new FileInputStream(f);
139: } catch (FileNotFoundException e) {
140: // ignore
141: }
142: } else {
143: is = config.getServletContext().getResourceAsStream(
144: relPath);
145: }
146:
147: }
148: if (is == null)
149: throw new ServletException(
150: "Unable to find Web service configuration file, webservice.xml.");
151:
152: //String path = config.getServletContext().getRealPath("/WEB-INF/webservice.xml");
153: //File configFile = new File(path);
154: //if (! configFile.exists()) {
155: // throw new ServletException ("Non-existent file: " + path);
156: //}
157:
158: Element root = null;
159:
160: // Parse webservice.xml file
161: try {
162:
163: DocumentBuilderFactory docFactory = DocumentBuilderFactory
164: .newInstance();
165: docFactory.setNamespaceAware(true);
166: DocumentBuilder docBuilder = docFactory
167: .newDocumentBuilder();
168: Document doc = docBuilder.parse(is);
169: root = doc.getDocumentElement();
170: } catch (Exception ex) {
171: fatal("Failed to parse webservice.xml file.", ex);
172: throw new ServletException(
173: "Failed to parse webservice.xml file.", ex);
174: } finally {
175: try {
176: if (is != null)
177: is.close();
178: } catch (Exception e) {
179: }
180: }
181:
182: // Create Web service context
183: Element contextElem = XMLUtil.getSubElement(root,
184: WebServiceServerConstants.SERVER_CONTEXT_TAG);
185: if (contextElem == null)
186: throw new ServletException(
187: "Missing Web server context configuration.");
188: wssServerContext = new HTTPWebServiceContext(rootDir);
189: wssServerContext.init(contextElem, config);
190:
191: // Create a UserManager
192: Element userManElem = XMLUtil.getSubElement(root,
193: WebServiceServerConstants.USER_MANAGER_TAG);
194: if (userManElem != null) {
195: String manClass = XMLUtil.getSubElementText(userManElem,
196: WebServiceServerConstants.CLASS_TAG);
197: if (manClass == null) {
198: throw new ServletException(
199: "Missing user manager class name.");
200: }
201:
202: try {
203: // I am not sure if this init scheme is the right thing to do. I could have defined
204: // a different init method for UserManager that takes just the servlet context,
205: // but I am sticking to the one defined in ServerObject interface and setting the
206: // UserAndRoleManager on server context object after initialization.
207: wssUserManager = (UserManager) Class.forName(manClass)
208: .newInstance();
209: } catch (InstantiationException e) {
210: throw new ServletException(
211: "Failed to create UserManager instance.", e);
212: } catch (IllegalAccessException e) {
213: throw new ServletException(
214: "Failed to create UserManager instance.", e);
215: } catch (ClassNotFoundException e) {
216: throw new ServletException(
217: "Failed to create UserManager instance.", e);
218: }
219: try {
220: wssUserManagerProps = WebServiceUtil
221: .deserializePropertiesInfo(userManElem);
222: wssUserManager.init(new ServerObjectConfigImpl(
223: wssServerContext, wssUserManagerProps));
224: } catch (WebServiceException e1) {
225: throw new ServletException(
226: "Failed to initialize UserAndRoleManager.", e1);
227: }
228: wssServerContext.setUserManager(wssUserManager);
229:
230: }
231:
232: // Create Web service directory
233: Element registryElem = XMLUtil.getSubElement(root,
234: WebServiceServerConstants.SERVICE_DIRECTORY_TAG);
235: if (registryElem == null) {
236: wssServiceDirectory = new WebServiceDirectoryImpl();
237: } else {
238: try {
239: wssServiceDirectory = (WebServiceDirectory) Class
240: .forName(
241: XMLUtil.getSubElementText(registryElem,
242: "class")).newInstance();
243: wssServiceDirectoryProps = WebServiceUtil
244: .deserializePropertiesInfo(registryElem);
245: wssServiceDirectory.init(new ServerObjectConfigImpl(
246: wssServerContext, wssServiceDirectoryProps));
247: } catch (InstantiationException ex) {
248: throw new ServletException(
249: "Failed to instantiate service registry.", ex);
250: } catch (IllegalAccessException ex) {
251: throw new ServletException(
252: "Access exception in instantiating service registry.",
253: ex);
254: } catch (ClassNotFoundException ex) {
255: throw new ServletException(
256: "Service registry class could not be found.",
257: ex);
258: } catch (WebServiceException ex) {
259: throw new ServletException(
260: "Failed to initialize Web service registry.",
261: ex);
262: }
263: }
264:
265: // Create instance of a request processor that takes care of marshalling and
266: // unmarshalling Web service requests.
267:
268: Element smElem = XMLUtil.getSubElement(root,
269: WebServiceServerConstants.REQUEST_PROCESSOR_TAG);
270: if (smElem == null) {
271: throw new ServletException(
272: "Missing request processor configuration.");
273: }
274: String req_handler_cl = XMLUtil.getSubElementText(smElem,
275: WebServiceServerConstants.CLASS_TAG);
276: if (req_handler_cl == null) {
277: throw new ServletException(
278: "Missing request processor class name.");
279: }
280:
281: try {
282: wssRequestProcessor = (ServletRequestProcessor) Class
283: .forName(req_handler_cl).newInstance();
284: } catch (Exception ex) {
285: throw new ServletException(
286: "Error in instantiating request processor.", ex);
287: }
288:
289: try {
290: wssRequestProcessorProps = WebServiceUtil
291: .deserializePropertiesInfo(smElem);
292: wssRequestProcessor.init(new ServerObjectConfigImpl(
293: wssServerContext, wssRequestProcessorProps));
294: // The following call sets a handler that marshals callbacks
295: // that need to be propogated to remote listeners over persistent
296: // HTTP connections
297: // RemoteCallbackProxy.setHandler(rsSM.getRPCHandler());
298: } catch (WebServiceException e) {
299: throw new ServletException(
300: "Failed to initialize request processor.", e);
301: }
302:
303: // Load web services
304:
305: try {
306: WebServiceManager.init(root, wssServiceDirectory,
307: wssServerContext, wssRequestProcessor);
308: } catch (WebServiceException ex) {
309: fatal("Failed to load Web services", ex);
310: throw new ServletException("Failed to load Web services.",
311: ex);
312: }
313:
314: // Load interceptors
315: try {
316: InterceptorManager.init(root, wssServerContext);
317: } catch (WebServiceException ex) {
318: fatal("Failed to initialize interceptor framework.", ex);
319: throw new ServletException(
320: "Failed to initialize interceptor framework.", ex);
321: }
322:
323: }
324:
325: public static WebServiceServlet getInstance() {
326: return wssSelf;
327: }
328:
329: /**
330: * Processes requests for service WSDLs
331: */
332: public void doGet(HttpServletRequest req, HttpServletResponse res)
333: throws ServletException, IOException {
334:
335: String reqURL = req.getRequestURL().toString();
336: String serviceName = StringUtil.extractLastToken(reqURL, "/");
337: try {
338: WebServiceInfo serviceInfo = WebServiceServlet
339: .getInstance().getWebServiceInfo(serviceName);
340: if (serviceInfo == null) {
341: reportNoWSDL(res, res.getWriter(), "No such service.",
342: null);
343: return;
344: }
345:
346: if (serviceInfo.wsdlFile != null) {
347: // TODO: Do the soap address location updates
348: String path = wssServerContext
349: .getRealPath(serviceInfo.wsdlFile);
350: String wsdlStr = IOUtil
351: .getFileContentAsString(new File(path));
352: res.setContentType("text/xml; charset="
353: + XMLUtils.getEncoding().toLowerCase());
354: res.getWriter().write(wsdlStr);
355: return;
356: }
357:
358: String serverURL = serviceInfo.requiresSecureAccess ? wssServerContext
359: .getSecureURL()
360: : wssServerContext.getUnsecureURL();
361: Document wsdlDoc = wssRequestProcessor.getWSDL(serviceInfo,
362: serverURL, reqURL);
363:
364: if (wsdlDoc != null) {
365: res.setContentType("text/xml; charset="
366: + XMLUtils.getEncoding().toLowerCase());
367: reportWSDL(wsdlDoc, res.getWriter());
368: } else {
369: warn("processWsdlRequest: failed to create WSDL");
370: reportNoWSDL(res, res.getWriter(), "WSDL not found.",
371: null);
372: }
373: } catch (WebServiceException e) {
374: res.setStatus(HttpURLConnection.HTTP_NOT_FOUND);
375: reportNoWSDL(res, res.getWriter(), "Internal error.", e);
376:
377: }
378:
379: }
380:
381: /**
382: * Report that we have no WSDL.
383: *
384: * @param res
385: * @param writer
386: * @param moreDetailCode message to provide more detail
387: */
388: public void reportNoWSDL(HttpServletResponse res,
389: PrintWriter writer, String moreDetailCode,
390: WebServiceException e) {
391: res.setStatus(HttpURLConnection.HTTP_NOT_FOUND);
392: res.setContentType("text/html");
393: writer.println("<h2> WSDL Request Failed.</h2>");
394: if (moreDetailCode != null) {
395: writer.println("<p>" + moreDetailCode + "</p>");
396: }
397: if (wssDevelopmentMode && e != null) {
398: String error = StringUtil.getErrorStackTraceString(e);
399: writer.println(error);
400: }
401: }
402:
403: /**
404: * Process incoming HTTP POST requests
405: *
406: * @param request Object that encapsulates the request to the servlet
407: * @param response Object that encapsulates the response from the servlet
408: */
409: public void doPost(HttpServletRequest req, HttpServletResponse res)
410: throws ServletException, IOException {
411:
412: try {
413: RequestInfo reqInfo = new RequestInfo();
414:
415: // Find target service
416: WebServiceInfo serviceInfo = find(req);
417: if (serviceInfo == null) {
418: wssRequestProcessor.processError(req, res,
419: "Service not found.");
420: return;
421: }
422:
423: if (serviceInfo.webService == null) {
424: wssRequestProcessor.processError(req, res,
425: "Service not loaded.");
426: return;
427: }
428:
429: if (!serviceInfo.isRunning) {
430: wssRequestProcessor.processError(req, res,
431: "Service not loaded.");
432: return;
433: }
434:
435: if (serviceInfo.requiresSecureAccess && !req.isSecure()) {
436: wssRequestProcessor.processError(req, res,
437: "Service requires secure access.");
438: return;
439: }
440: reqInfo.serviceInfo = serviceInfo;
441: reqInfo.webServiceRequest = Current.getRequestContext()
442: .getWebServiceRequest();
443: wssRequestProcessor.process(req, res, reqInfo);
444:
445: } catch (WebServiceException ex1) {
446: error("Exception in processing " + req.getQueryString()
447: + " from user " + req.getRemoteUser(), ex1);
448: throw new ServletException(ex1.getMessage(), ex1.getCause());
449: } catch (IOException ex2) {
450: error("Exception in processing " + req.getQueryString()
451: + " from user " + req.getRemoteUser(), ex2);
452:
453: throw ex2;
454: } finally {
455: Current.detachRequestContext();
456: }
457: }
458:
459: public void save() throws WebServiceException, IOException {
460: final String indent = " ";
461: final String delta = " ";
462: //final String indent1 = " ";
463: //String path = getServletConfig().getServletContext().getRealPath("/WEB-INF/webservice.xml");
464: FileOutputStream fos = new FileOutputStream(wssConfigFilePath);
465: OutputStreamWriter writer = new OutputStreamWriter(fos, "UTF-8");
466: writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
467: writer.write(XMLUtil.LINE_FEED);
468: XMLUtil.writeElementStart("",
469: WebServiceServerConstants.WEB_SERVICE_CONFIG_TAG,
470: writer);
471: wssServerContext.serialize(indent, delta, writer);
472: WebServiceUtil.serializeBaseObject(wssUserManager, null,
473: WebServiceServerConstants.USER_MANAGER_TAG,
474: wssUserManagerProps, indent, delta, writer);
475: WebServiceUtil.serializeBaseObject(wssServiceDirectory, null,
476: WebServiceServerConstants.SERVICE_DIRECTORY_TAG,
477: wssServiceDirectoryProps, indent, delta, writer);
478: WebServiceUtil.serializeBaseObject(wssRequestProcessor, null,
479: WebServiceServerConstants.REQUEST_PROCESSOR_TAG,
480: wssRequestProcessorProps, indent, delta, writer);
481:
482: InterceptorManager.serialize(indent, delta, writer);
483:
484: WebServiceManager.serialize(indent, delta, writer);
485:
486: XMLUtil.writeElementEnd("",
487: WebServiceServerConstants.WEB_SERVICE_CONFIG_TAG,
488: writer);
489: writer.flush();
490: writer.close();
491: }
492:
493: public WebServiceInfo find(HttpServletRequest request)
494: throws WebServiceException {
495: String requestURI = request.getRequestURL().toString();
496: String serviceName = requestURI.substring(requestURI
497: .lastIndexOf("/") + 1);
498: return wssServiceDirectory.lookup(serviceName);
499: }
500:
501: public WebServiceInfo getWebServiceInfo(String name)
502: throws WebServiceException {
503: return wssServiceDirectory.lookup(name);
504: }
505:
506: /**
507: * Report WSDL.
508: *
509: * @param doc
510: * @param writer
511: */
512: public void reportWSDL(Document doc, PrintWriter writer) {
513: XMLUtils.PrettyDocumentToWriter(doc, writer);
514: }
515:
516: public void destroy() {
517: WebServiceManager.destroy();
518: }
519:
520: }
|