001: /*
002: * (C) Copyright 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.IOException;
022: import java.io.StringWriter;
023: import java.io.Writer;
024:
025: import org.w3c.dom.Element;
026:
027: import com.nabhinc.util.DOM2Writer;
028: import com.nabhinc.util.XMLUtil;
029: import com.nabhinc.ws.core.PropertyInfo;
030: import com.nabhinc.ws.core.WebServiceException;
031: import com.nabhinc.ws.core.WebServiceUtil;
032: import com.nabhinc.ws.service.core.BaseJavaWebService;
033:
034: /**
035: * Maintains metadata as well as a running instance of a Web
036: * service.
037: *
038: * @author Padmanabh Dabke
039: *
040: */
041: public class WebServiceInfo {
042:
043: private RequestProcessor requestProcessor = null;
044:
045: // Persistent attributes
046:
047: /**
048: * Web service name
049: */
050: public String name = null;
051:
052: /**
053: * Web service display name
054: */
055: public String displayName = null;
056:
057: /**
058: * Java class for the Web service
059: */
060: public String webServiceClass = null;
061:
062: /**
063: * Web service description
064: */
065: public String description = null;
066:
067: /**
068: * Application dependent Web service type.
069: */
070: public String type = null;
071:
072: /**
073: * Flag indicating if the Web service should
074: * be loaded at start up. Set this value to true for
075: * manual loading.
076: */
077: public boolean manualLoad = false;
078:
079: /**
080: * Flag indicating if this Web service is critical to
081: * the operation of the portal server. If it is marked
082: * critical, the Servlet loading the Web services will
083: * fail.
084: */
085: public boolean isCritical = false;
086:
087: /**
088: * Web service configuration properties.
089: */
090: public PropertyInfo[] propertiesInfo = null;
091:
092: /**
093: * "Run-as" user for this Web service.
094: */
095: public String owner = null;
096:
097: /**
098: * XML configuration for the Web service
099: */
100: public Element xmlConfig = null;
101:
102: /**
103: * Path of WSDL file if available
104: */
105: public String wsdlFile = null;
106:
107: /**
108: * Flag indicating if this service requires secure access
109: */
110: public boolean requiresSecureAccess = false;
111:
112: /**
113: * Class that provides the "signature" for
114: * Web service operations. This class is deduced from the mapping
115: * between the class implementing <code>WebService</code> interface
116: * and the name of the property that provides the workhorse class.
117: */
118: public String webServiceInterfaceClass = null;
119:
120: // Transient attributes
121:
122: /**
123: * Web service context
124: */
125: public WebServiceContext webServiceContext = null;
126:
127: /**
128: * Web service load status
129: */
130: public int status = WebServiceServerConstants.LOAD_STATUS_UNLOADED;
131:
132: /**
133: * Is associated Web service running. A service may be loaded and suspended
134: * via <code>stop()</code> call on the service. If the service is stopped,
135: * this flag will be false.
136: */
137: public boolean isRunning = false;
138: /**
139: * Error thrown in initialization or UnavailableException thrown
140: * during a method invocation.
141: */
142: public Throwable loadError = null;
143:
144: /**
145: * The last time this Web service was loaded. 0 if this service
146: * has not been loaded.
147: */
148: public long loadTime = 0;
149:
150: /**
151: * Web service instance. This is null if the service is not loaded.
152: */
153: public WebService webService = null;
154:
155: /**
156: * Initializes WebServiceInfo object from XML configuration.
157: * @param config XML configuration
158: * @param wsContext Web service context
159: * @param registry Service directory
160: * @param reqProcessor Request processor that takes care of service method
161: * invocations
162: * @throws WebServiceException Thrown if the Web service name is not specified
163: * or the configuration properties cannot be loaded.
164: */
165: public void init(Element config, WebServiceContext wsContext,
166: RequestProcessor reqProcessor) throws WebServiceException {
167: webServiceContext = wsContext;
168: name = XMLUtil.getSubElementText(config,
169: WebServiceServerConstants.NAME_TAG);
170: displayName = XMLUtil.getSubElementText(config,
171: WebServiceServerConstants.DISPLAY_NAME_TAG);
172: webServiceClass = XMLUtil.getSubElementText(config,
173: WebServiceServerConstants.CLASS_TAG);
174: webServiceInterfaceClass = XMLUtil.getSubElementText(config,
175: WebServiceServerConstants.SERVICE_INTERFACE_CLASS_TAG);
176: description = XMLUtil.getSubElementText(config,
177: WebServiceServerConstants.DESCR_TAG);
178: type = XMLUtil.getSubElementText(config,
179: WebServiceServerConstants.TYPE_TAG);
180: manualLoad = XMLUtil.getSubElement(config,
181: WebServiceServerConstants.MANUAL_LOAD_TAG) != null;
182: isCritical = XMLUtil.getSubElement(config,
183: WebServiceServerConstants.CRITICAL_TAG) != null;
184: owner = XMLUtil.getSubElementText(config,
185: WebServiceServerConstants.OWNER_TAG);
186: wsdlFile = XMLUtil.getSubElementText(config,
187: WebServiceServerConstants.WSDL_FILE_TAG);
188: requiresSecureAccess = XMLUtil.getSubElement(config,
189: WebServiceServerConstants.REQUIRES_SECURE_ACCESS_TAG) != null;
190: if (name == null)
191: throw new WebServiceException("Missing Web service name.");
192: if (webServiceClass == null)
193: throw new WebServiceException(
194: "Missing Web service class for service " + name
195: + ".");
196: propertiesInfo = WebServiceUtil
197: .deserializePropertiesInfo(config);
198: xmlConfig = XMLUtil.getSubElement(config, "deployment");
199: requestProcessor = reqProcessor;
200:
201: }
202:
203: /**
204: * Initializes WebServiceInfo from parameters passed via HTTP request.
205: * This method supports UI editing of Web service information.
206: * @param request HTTP request
207: * @param wsContext Web service context
208: * @param registry Service directory
209: * @param reqProcessor Request processor responsible for service method
210: * invocations.
211: * @throws WebServiceException
212: */
213: public void init(WebServiceContext wsContext,
214: RequestProcessor reqProcessor) throws WebServiceException {
215: webServiceContext = wsContext;
216: if (name == null)
217: throw new WebServiceException("Missing Web service name.");
218: if (webServiceClass == null)
219: throw new WebServiceException(
220: "Missing Web service class for service " + name
221: + ".");
222: requestProcessor = reqProcessor;
223:
224: }
225:
226: /**
227: * Serializes WebServiceInfo in XML.
228: * @param indent Initial indent used for formatting XML
229: * @param delta Indent for going to the next element level
230: * @param w Writer used for writing serialized state
231: * @throws IOException
232: */
233: public void serialize(String indent, String delta, Writer w)
234: throws IOException {
235: XMLUtil.writeElementStart(indent,
236: WebServiceServerConstants.WEB_SERVICE_TAG, w);
237: String indent1 = indent + delta;
238: XMLUtil.writeElement(indent1,
239: WebServiceServerConstants.NAME_TAG, name, w);
240: XMLUtil.writeElement(indent1,
241: WebServiceServerConstants.DISPLAY_NAME_TAG,
242: displayName, w);
243: XMLUtil
244: .writeElement(indent1,
245: WebServiceServerConstants.CLASS_TAG,
246: webServiceClass, w);
247: XMLUtil.writeElement(indent1,
248: WebServiceServerConstants.SERVICE_INTERFACE_CLASS_TAG,
249: webServiceInterfaceClass, w);
250: XMLUtil.writeElement(indent1,
251: WebServiceServerConstants.DESCR_TAG, description, w);
252: XMLUtil.writeElement(indent1,
253: WebServiceServerConstants.TYPE_TAG, type, w);
254: XMLUtil.writeElement(indent1,
255: WebServiceServerConstants.OWNER_TAG, owner, w);
256: if (manualLoad)
257: XMLUtil.writeEmptyElement(indent1,
258: WebServiceServerConstants.MANUAL_LOAD_TAG, w);
259: if (isCritical)
260: XMLUtil.writeEmptyElement(indent1,
261: WebServiceServerConstants.CRITICAL_TAG, w);
262: if (requiresSecureAccess)
263: XMLUtil
264: .writeEmptyElement(
265: indent1,
266: WebServiceServerConstants.REQUIRES_SECURE_ACCESS_TAG,
267: w);
268: WebServiceUtil.serializePropertiesInfo(propertiesInfo, indent1,
269: delta, w);
270: if (xmlConfig != null)
271: DOM2Writer.serializeAsXML(xmlConfig, w, true, false,
272: "UTF-8", indent1.length(), delta.length());
273:
274: // requestProcessor.serialize(xmlConfig, indent1, delta, w);
275: XMLUtil.writeElementEnd(indent,
276: WebServiceServerConstants.WEB_SERVICE_TAG, w);
277: }
278:
279: /**
280: * Attempts to load associated Web service if it is not loaded.
281: * @throws WebServiceException
282: */
283: public void load() throws WebServiceException {
284:
285: // If webService is not null, assume that it is successfully
286: // loaded. Just return;
287: if (webService != null)
288: return;
289:
290: try {
291: // Set the status to error here. If things go smoothly, it will be
292: // set to loaded at the end of the try block.
293: status = WebServiceServerConstants.LOAD_STATUS_INIT_ERROR;
294: WebService ws = (WebService) Class.forName(webServiceClass)
295: .newInstance();
296: ws.init(new ServerObjectConfigImpl(webServiceContext,
297: propertiesInfo));
298: ws.start();
299: webService = ws;
300: isRunning = true;
301: status = WebServiceServerConstants.LOAD_STATUS_LOADED;
302: loadError = null;
303: requestProcessor.load(this , xmlConfig);
304: } catch (ClassNotFoundException ex) {
305: loadError = ex;
306: throw new WebServiceException("Invalid class "
307: + webServiceClass + " for Web service " + name
308: + ".", ex);
309: } catch (InstantiationException ex) {
310: loadError = ex;
311: throw new WebServiceException(
312: "Instantiation error in creating instance of class "
313: + webServiceClass + " for Web service "
314: + name + ".", ex);
315: } catch (ClassCastException ex) {
316: loadError = ex;
317: throw new WebServiceException(
318: "Class must implement WebService interface.", ex);
319: } catch (IllegalAccessException ex) {
320: loadError = ex;
321: throw new WebServiceException(
322: "Illegal access error in creating instance of class "
323: + webServiceClass + " for Web service "
324: + name + ".", ex);
325: } catch (WebServiceException ex) {
326: loadError = ex;
327: throw ex;
328: } catch (Throwable t) {
329: loadError = t;
330: throw new WebServiceException(
331: "Failed to initialize web service " + this .name
332: + ".", t);
333: }
334: }
335:
336: /**
337: * Unloads associated service (if loaded) and loads it again.
338: * @throws WebServiceException
339: */
340: public void reload() throws WebServiceException {
341: unload();
342: load();
343: }
344:
345: /**
346: * Unloads associated Web service.
347: *
348: */
349: public void unload() {
350: if (webService != null) {
351: status = WebServiceServerConstants.LOAD_STATUS_UNLOADED;
352: webService.stop();
353: webService.destroy();
354: webService = null;
355: isRunning = false;
356: requestProcessor.unload(this );
357: }
358:
359: }
360:
361: public synchronized void start() throws WebServiceException {
362: if (webService != null && !isRunning) {
363: webService.start();
364: isRunning = true;
365: }
366: }
367:
368: public synchronized void stop() throws WebServiceException {
369: if (webService != null && isRunning) {
370: webService.stop();
371: isRunning = false;
372: }
373: }
374:
375: /**
376: * Stores the new property information. If the associated Web service
377: * is running, sets it's properties.
378: * @param newProps New property info
379: * @throws WebServiceException
380: */
381: public void setPropertiesInfo(PropertyInfo[] newProps)
382: throws WebServiceException {
383: if (webService != null) {
384: webService.setProperties(newProps);
385: }
386: propertiesInfo = newProps;
387:
388: }
389:
390: public String computeServiceInterfaceClass()
391: throws WebServiceException {
392: if (webServiceInterfaceClass != null)
393: return webServiceInterfaceClass;
394: try {
395: // First check if the Web service is a sub-class of BaseJavaWebService
396: // In this case, the implementation class is same as the web service
397: // class.
398: Class wsClass = Class.forName(webServiceClass);
399: if (BaseJavaWebService.class.isAssignableFrom(wsClass)) {
400: if (java.rmi.Remote.class.isAssignableFrom(wsClass)) {
401: String clStr = computeRemoteInterface(wsClass);
402: if (clStr != null)
403: return clStr;
404: }
405: return webServiceClass;
406: }
407: String implClassProp = WebServiceManager
408: .getServiceImplClassProperty(webServiceClass);
409: if (implClassProp == null)
410: return null;
411: if (propertiesInfo != null) {
412: for (int i = 0; i < propertiesInfo.length; i++) {
413: if (propertiesInfo[i].name.equals(implClassProp)) {
414: return propertiesInfo[i].value;
415: }
416: }
417: }
418: return null;
419: } catch (ClassNotFoundException e) {
420: throw new WebServiceException("Invalid service class.", e);
421: }
422:
423: }
424:
425: public String getProcessorConfiguration() throws IOException {
426: StringWriter writer = new StringWriter();
427: requestProcessor.serialize(xmlConfig, "", " ", writer);
428: writer.flush();
429: return writer.toString();
430: }
431:
432: private String computeRemoteInterface(Class implClass)
433: throws ClassNotFoundException {
434:
435: Class[] intClasses = implClass.getInterfaces();
436: if (intClasses == null)
437: return null;
438: for (int i = 0; i < intClasses.length; i++) {
439: if (java.rmi.Remote.class.isAssignableFrom(intClasses[i])
440: && !java.rmi.Remote.class.equals(intClasses[i]))
441: return intClasses[i].getName();
442: }
443: return null;
444: }
445:
446: }
|