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: package org.apache.cocoon.components.axis;
018:
019: import java.io.File;
020: import java.io.InputStreamReader;
021: import java.util.ArrayList;
022: import java.util.HashMap;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Map;
026:
027: import javax.servlet.ServletContext;
028: import javax.servlet.http.HttpServletRequest;
029: import javax.servlet.http.HttpServletResponse;
030:
031: import org.apache.avalon.framework.activity.Initializable;
032: import org.apache.avalon.framework.activity.Startable;
033: import org.apache.avalon.framework.component.Component;
034: import org.apache.avalon.framework.component.ComponentException;
035: import org.apache.avalon.framework.component.ComponentManager;
036: import org.apache.avalon.framework.component.Composable;
037: import org.apache.avalon.framework.configuration.Configurable;
038: import org.apache.avalon.framework.configuration.Configuration;
039: import org.apache.avalon.framework.configuration.ConfigurationException;
040: import org.apache.avalon.framework.context.Context;
041: import org.apache.avalon.framework.context.ContextException;
042: import org.apache.avalon.framework.context.Contextualizable;
043: import org.apache.avalon.framework.logger.AbstractLogEnabled;
044: import org.apache.avalon.framework.thread.ThreadSafe;
045: import org.apache.axis.AxisEngine;
046: import org.apache.axis.Constants;
047: import org.apache.axis.EngineConfiguration;
048: import org.apache.axis.MessageContext;
049: import org.apache.axis.configuration.FileProvider;
050: import org.apache.axis.deployment.wsdd.WSDDDeployment;
051: import org.apache.axis.deployment.wsdd.WSDDDocument;
052: import org.apache.axis.deployment.wsdd.WSDDService;
053: import org.apache.axis.security.servlet.ServletSecurityProvider;
054: import org.apache.axis.server.AxisServer;
055: import org.apache.axis.transport.http.HTTPConstants;
056: import org.apache.axis.transport.http.HTTPTransport;
057: import org.apache.axis.transport.http.ServletEndpointContextImpl;
058: import org.apache.axis.utils.XMLUtils;
059: import org.apache.cocoon.components.axis.providers.AvalonProvider;
060: import org.apache.cocoon.util.IOUtils;
061: import org.apache.commons.lang.BooleanUtils;
062: import org.apache.excalibur.source.Source;
063: import org.apache.excalibur.source.SourceResolver;
064: import org.apache.excalibur.xml.dom.DOMParser;
065: import org.w3c.dom.Document;
066: import org.xml.sax.InputSource;
067:
068: /**
069: * SOAP Server Implementation
070: *
071: * <p>
072: * This server accepts a SOAP Request, and generates the resultant
073: * response as output. Essentially, this reader allows you to serve SOAP
074: * requests from your Cocoon application.
075: * </p>
076: *
077: * <p>
078: * Code originates from the Apache
079: * <a href="http://xml.apache.org/axis">AXIS</a> project,
080: * <code>org.apache.axis.http.transport.AxisServlet</code>.
081: * </p>
082: *
083: * Ported to Cocoon by:
084: *
085: * @author <a href="mailto:crafterm@apache.org">Marcus Crafter</a>
086: *
087: * Original <code>AxisServlet</code> authors:
088: *
089: * @author <a href="mailto:">Steve Loughran</a>
090: * @author <a href="mailto:dug@us.ibm.com">Doug Davis</a>
091: *
092: * @version CVS $Id: SoapServerImpl.java 540711 2007-05-22 19:36:07Z cziegeler $
093: */
094: public class SoapServerImpl extends AbstractLogEnabled implements
095: SoapServer, Composable, Configurable, Contextualizable,
096: Initializable, Startable, ThreadSafe {
097:
098: /**
099: * Constant describing the default location of the server configuration file
100: */
101: public static final String DEFAULT_SERVER_CONFIG = "resource://org/apache/axis/server/server-config.wsdd";
102:
103: // transport name
104: private String m_transportName;
105:
106: // security provider reference
107: private ServletSecurityProvider m_securityProvider;
108:
109: // JWS output directory
110: private String m_jwsClassDir;
111:
112: // per-instance cache of the axis server
113: private AxisServer m_axisServer;
114:
115: // axis server configuration
116: private FileProvider m_engineConfig;
117:
118: // location of attachments
119: private String m_attachmentDir;
120:
121: // server configuration
122: private Source m_serverWSDD;
123:
124: // array containing locations to descriptors this reader should manage
125: private WSDDDocument[] m_descriptors;
126:
127: // context reference
128: private Context context;
129:
130: // component manager reference
131: private ComponentManager manager;
132:
133: /* (non-Javadoc)
134: * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
135: */
136: public void contextualize(final Context context)
137: throws ContextException {
138: this .context = context;
139: }
140:
141: /* (non-Javadoc)
142: * @see org.apache.avalon.framework.component.Composable#compose(org.apache.avalon.framework.component.ComponentManager)
143: */
144: public void compose(ComponentManager manager)
145: throws ComponentException {
146: this .manager = manager;
147: }
148:
149: /**
150: * Configures this server.
151: *
152: * <p>
153: * Sets the following optional configuration settings:
154: *
155: * <ul>
156: * <li>Server WSDD configuration
157: * <li>Attachment directory
158: * <li>JWS directory
159: * <li>Security provider
160: * <li>Transport name
161: * <li>Mananged services
162: * </ul>
163: * </p>
164: *
165: * <p>
166: * The following format is used:
167: * <pre>
168: * <soap-server>
169: * <server-wsdd src="..."/>
170: * <attachment-dir src="..."/>
171: * <jws-dir src="..."/>
172: * <security-provider enabled="..."/>
173: * <transport name="..."/>
174: * <managed-services>
175: * <descriptor src="..."/>
176: * <descriptor src="..."/>
177: * </managed-services>
178: * </soap-server>
179: * </pre>
180: * </p>
181: *
182: * @param config a <code>Configuration</code> instance
183: * @exception ConfigurationException if an error occurs
184: */
185: public void configure(final Configuration config)
186: throws ConfigurationException {
187: try {
188: this .setServerConfig(config);
189: this .setAttachmentDir(config);
190: this .setJWSDir(config);
191: this .setSecurityProvider(config);
192: this .setTransportName(config);
193: this .setManagedServices(config);
194:
195: if (this .getLogger().isDebugEnabled()) {
196: this .getLogger().debug(
197: "SoapServerImpl.configure() complete");
198: }
199: } catch (final Exception e) {
200: throw new ConfigurationException(
201: "Error during configuration", e);
202: }
203: }
204:
205: /**
206: * Helper method to set the axis server configuration.
207: *
208: * @param config a <code>Configuration</code> instance
209: * @exception Exception if an error occurs
210: */
211: public void setServerConfig(final Configuration config)
212: throws Exception {
213: final Configuration wsdd = config.getChild("server-wsdd");
214: SourceResolver resolver = null;
215:
216: try {
217: resolver = (SourceResolver) this .manager
218: .lookup(SourceResolver.ROLE);
219: this .m_serverWSDD = resolver.resolveURI(wsdd.getAttribute(
220: "src", DEFAULT_SERVER_CONFIG));
221: } finally {
222: this .manager.release((Component) resolver);
223: }
224: }
225:
226: /**
227: * Helper method to set the attachment dir. If no attachment directory has
228: * been specified, then its set up to operate out of the Cocoon workarea.
229: *
230: * @param config a <code>Configuration</code> instance
231: * @exception ConfigurationException if a configuration error occurs
232: * @exception ContextException if a context error occurs
233: */
234: private void setAttachmentDir(final Configuration config)
235: throws ConfigurationException, ContextException {
236: final Configuration dir = config.getChild("attachment-dir");
237: this .m_attachmentDir = dir.getAttribute("src", null);
238:
239: if (this .m_attachmentDir == null) {
240: File workDir = (File) this .context
241: .get(org.apache.cocoon.Constants.CONTEXT_WORK_DIR);
242: File attachmentDir = IOUtils.createFile(workDir,
243: "attachments" + File.separator);
244: this .m_attachmentDir = IOUtils
245: .getFullFilename(attachmentDir);
246: }
247:
248: if (this .getLogger().isDebugEnabled()) {
249: this .getLogger().debug(
250: "attachment directory = " + this .m_attachmentDir);
251: }
252: }
253:
254: /**
255: * Helper method to set the JWS class dir. If no directory is specified then
256: * the directory <i>axis-jws</i> is used, under the Cocoon workarea.
257: *
258: * @param config a <code>Configuration</code> instance
259: * @exception ConfigurationException if a configuration error occurs
260: * @exception ContextException if a context error occurs
261: */
262: private void setJWSDir(final Configuration config)
263: throws ConfigurationException, ContextException {
264: final Configuration dir = config.getChild("jws-dir");
265: this .m_jwsClassDir = dir.getAttribute("src", null);
266:
267: if (this .m_jwsClassDir == null) {
268: File workDir = (File) this .context
269: .get(org.apache.cocoon.Constants.CONTEXT_WORK_DIR);
270: File jwsClassDir = IOUtils.createFile(workDir, "axis-jws"
271: + File.separator);
272: this .m_jwsClassDir = IOUtils.getFullFilename(jwsClassDir);
273: }
274:
275: if (this .getLogger().isDebugEnabled()) {
276: this .getLogger().debug(
277: "jws class directory = " + this .m_jwsClassDir);
278: }
279: }
280:
281: /**
282: * Helper method to set the security provider.
283: *
284: * @param config a <code>Configuration</code> instance
285: * @exception ConfigurationException if an error occurs
286: */
287: private void setSecurityProvider(final Configuration config)
288: throws ConfigurationException {
289: final Configuration secProvider = config.getChild(
290: "security-provider", false);
291:
292: if (secProvider != null) {
293: final String attr = secProvider.getAttribute("enabled");
294: final boolean providerIsEnabled = BooleanUtils
295: .toBoolean(attr);
296:
297: if (providerIsEnabled) {
298: this .m_securityProvider = new ServletSecurityProvider();
299: }
300: }
301:
302: if (this .getLogger().isDebugEnabled()) {
303: this .getLogger().debug(
304: "security provider = " + this .m_securityProvider);
305: }
306: }
307:
308: /**
309: * Helper method to set the transport name
310: *
311: * @param config a <code>Configuration</code> instance
312: * @exception ConfigurationException if an error occurs
313: */
314: private void setTransportName(final Configuration config)
315: throws ConfigurationException {
316: final Configuration name = config.getChild("transport");
317: this .m_transportName = name.getAttribute("name",
318: HTTPTransport.DEFAULT_TRANSPORT_NAME);
319: }
320:
321: /**
322: * Helper method to obtain a list of managed services from the given
323: * configuration (ie. locations of deployement descriptors to be
324: * deployed).
325: *
326: * @param config a <code>Configuration</code> value
327: * @exception Exception if an error occurs
328: */
329: private void setManagedServices(final Configuration config)
330: throws Exception {
331: final Configuration m = config.getChild("managed-services",
332: false);
333: final List descriptors = new ArrayList();
334:
335: if (m != null) {
336: SourceResolver resolver = null;
337: DOMParser parser = null;
338:
339: try {
340: final Configuration[] services = m
341: .getChildren("descriptor");
342: resolver = (SourceResolver) this .manager
343: .lookup(SourceResolver.ROLE);
344: parser = (DOMParser) this .manager
345: .lookup(DOMParser.ROLE);
346:
347: for (int i = 0; i < services.length; ++i) {
348: final String location = services[i]
349: .getAttribute("src");
350: Source source = resolver.resolveURI(location);
351:
352: final Document d = parser
353: .parseDocument(new InputSource(
354: new InputStreamReader(source
355: .getInputStream())));
356:
357: descriptors.add(new WSDDDocument(d));
358: }
359: } finally {
360: this .manager.release((Component) resolver);
361: this .manager.release((Component) parser);
362: }
363: }
364:
365: // convert the list of descriptors to an array, for easier iteration
366: this .m_descriptors = (WSDDDocument[]) descriptors
367: .toArray(new WSDDDocument[] {});
368: }
369:
370: /* (non-Javadoc)
371: * @see org.apache.avalon.framework.activity.Initializable#initialize()
372: */
373: public void initialize() throws Exception {
374: this .m_axisServer = this .createEngine();
375:
376: if (this .getLogger().isDebugEnabled()) {
377: this .getLogger().debug(
378: "SoapServerImpl.initialize() complete");
379: }
380: }
381:
382: /**
383: * Starts this server. Deploys all managed services as specified at
384: * configuration time.
385: *
386: * @exception Exception if an error occurs
387: */
388: public void start() throws Exception {
389: // deploy all configured services
390: for (int i = 0; i < this .m_descriptors.length; ++i) {
391: WSDDDeployment deployment = this .m_engineConfig
392: .getDeployment();
393: this .m_descriptors[i].deploy(deployment);
394:
395: if (this .getLogger().isDebugEnabled()) {
396: this
397: .getLogger()
398: .debug(
399: "Deployed Descriptor:\n"
400: + XMLUtils
401: .DocumentToString(this .m_descriptors[i]
402: .getDOMDocument()));
403: }
404: }
405:
406: if (this .getLogger().isDebugEnabled()) {
407: this .getLogger().debug("SoapServerImpl.start() complete");
408: }
409: }
410:
411: /**
412: * Stops this reader. Undeploys all managed services this reader
413: * currently manages (includes services dynamically added to the reader
414: * during runtime).
415: *
416: * @exception Exception if an error occurs
417: */
418: public void stop() throws Exception {
419: WSDDDeployment deployment = this .m_engineConfig.getDeployment();
420: WSDDService[] services = deployment.getServices();
421:
422: // undeploy all deployed services
423: for (int i = 0; i < services.length; ++i) {
424: deployment.undeployService(services[i].getQName());
425:
426: if (this .getLogger().isDebugEnabled()) {
427: this .getLogger().debug(
428: "Undeployed: " + services[i].toString());
429: }
430: }
431:
432: if (this .getLogger().isDebugEnabled()) {
433: this .getLogger().debug("SoapServerImpl.stop() complete");
434: }
435: }
436:
437: /* (non-Javadoc)
438: * @see org.apache.cocoon.components.axis.SoapServer#invoke(org.apache.axis.MessageContext)
439: */
440: public void invoke(MessageContext message) throws Exception {
441: this .m_axisServer.invoke(message);
442: }
443:
444: /**
445: * Place the Request message in the MessagContext object - notice
446: * that we just leave it as a 'ServletRequest' object and let the
447: * Message processing routine convert it - we don't do it since we
448: * don't know how it's going to be used - perhaps it might not
449: * even need to be parsed.
450: */
451: public MessageContext createMessageContext(HttpServletRequest req,
452: HttpServletResponse res, ServletContext con) {
453:
454: MessageContext msgContext = new MessageContext(
455: this .m_axisServer);
456: String webInfPath = con.getRealPath("/WEB-INF");
457: String homeDir = con.getRealPath("/");
458:
459: // Set the Transport
460: msgContext.setTransportName(this .m_transportName);
461:
462: // Add Avalon specifics to MessageContext
463: msgContext.setProperty(LOGGER, this .getLogger());
464: msgContext.setProperty(AvalonProvider.COMPONENT_MANAGER,
465: this .manager);
466:
467: // Save some HTTP specific info in the bag in case someone needs it
468: msgContext.setProperty(Constants.MC_JWS_CLASSDIR,
469: this .m_jwsClassDir);
470: msgContext.setProperty(Constants.MC_HOME_DIR, homeDir);
471: msgContext.setProperty(Constants.MC_RELATIVE_PATH, req
472: .getServletPath());
473: msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLET, this );
474: msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST,
475: req);
476: msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETRESPONSE,
477: res);
478: msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETLOCATION,
479: webInfPath);
480: msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETPATHINFO,
481: req.getPathInfo());
482: msgContext.setProperty(HTTPConstants.HEADER_AUTHORIZATION, req
483: .getHeader(HTTPConstants.HEADER_AUTHORIZATION));
484: msgContext.setProperty(Constants.MC_REMOTE_ADDR, req
485: .getRemoteAddr());
486:
487: // Set up a javax.xml.rpc.server.ServletEndpointContext
488: ServletEndpointContextImpl sec = new ServletEndpointContextImpl();
489: msgContext.setProperty(Constants.MC_SERVLET_ENDPOINT_CONTEXT,
490: sec);
491:
492: // Save the real path
493: String realpath = con.getRealPath(req.getServletPath());
494:
495: if (realpath != null) {
496: msgContext.setProperty(Constants.MC_REALPATH, realpath);
497: }
498:
499: msgContext.setProperty(Constants.MC_CONFIGPATH, webInfPath);
500:
501: if (this .m_securityProvider != null) {
502: msgContext.setProperty("securityProvider",
503: this .m_securityProvider);
504: }
505:
506: // write out the contents of the message context for debugging purposes
507: if (this .getLogger().isDebugEnabled()) {
508: this .debugMessageContext(msgContext);
509: }
510:
511: return msgContext;
512: }
513:
514: /**
515: * Helper method to log the contents of a given message context
516: *
517: * @param context a <code>MessageContext</code> instance
518: */
519: private void debugMessageContext(final MessageContext context) {
520: for (final Iterator i = context.getPropertyNames(); i.hasNext();) {
521: final String key = (String) i.next();
522: this .getLogger().debug(
523: "MessageContext: Key:" + key + ": Value: "
524: + context.getProperty(key));
525: }
526: }
527:
528: /**
529: * This is a uniform method of initializing AxisServer in a servlet
530: * context.
531: */
532: public AxisServer createEngine() throws Exception {
533: AxisServer engine = AxisServer.getServer(this
534: .getEngineEnvironment());
535:
536: if (this .getLogger().isDebugEnabled()) {
537: this .getLogger().debug("Axis engine created");
538: }
539:
540: return engine;
541: }
542:
543: protected Map getEngineEnvironment() throws Exception {
544: Map env = new HashMap();
545:
546: // use FileProvider directly with a Avalon Source object instead of going
547: // through the EngineConfigurationFactoryServlet class
548: this .m_engineConfig = new FileProvider(this .m_serverWSDD
549: .getInputStream());
550:
551: env.put(EngineConfiguration.PROPERTY_NAME, this .m_engineConfig);
552: env.put(AxisEngine.ENV_ATTACHMENT_DIR, this .m_attachmentDir);
553: // REVISIT(MC): JNDI Factory support ?
554: //env.put(AxisEngine.ENV_SERVLET_CONTEXT, context);
555:
556: return env;
557: }
558: }
|