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
018 package org.apache.geronimo.axis2;
019
020 import java.io.ByteArrayInputStream;
021 import java.io.PrintWriter;
022 import java.net.HttpURLConnection;
023 import java.net.URL;
024 import java.util.List;
025
026 import javax.naming.Context;
027 import javax.servlet.ServletContext;
028 import javax.servlet.http.HttpServletRequest;
029 import javax.servlet.http.HttpServletResponse;
030 import javax.xml.ws.Binding;
031 import javax.xml.ws.WebServiceException;
032 import javax.xml.ws.handler.Handler;
033
034 import org.apache.axiom.om.util.UUIDGenerator;
035 import org.apache.axis2.AxisFault;
036 import org.apache.axis2.Constants;
037 import org.apache.axis2.addressing.AddressingHelper;
038 import org.apache.axis2.addressing.EndpointReference;
039 import org.apache.axis2.context.ConfigurationContext;
040 import org.apache.axis2.context.ConfigurationContextFactory;
041 import org.apache.axis2.context.MessageContext;
042 import org.apache.axis2.context.OperationContext;
043 import org.apache.axis2.description.AxisService;
044 import org.apache.axis2.description.TransportInDescription;
045 import org.apache.axis2.description.TransportOutDescription;
046 import org.apache.axis2.engine.AxisEngine;
047 import org.apache.axis2.engine.Handler.InvocationResponse;
048 import org.apache.axis2.jaxws.binding.BindingImpl;
049 import org.apache.axis2.jaxws.binding.BindingUtils;
050 import org.apache.axis2.jaxws.description.EndpointDescription;
051 import org.apache.axis2.jaxws.description.impl.DescriptionUtils;
052 import org.apache.axis2.jaxws.description.xml.handler.HandlerChainType;
053 import org.apache.axis2.jaxws.description.xml.handler.HandlerChainsType;
054 import org.apache.axis2.jaxws.description.xml.handler.HandlerType;
055 import org.apache.axis2.jaxws.handler.lifecycle.factory.HandlerLifecycleManagerFactory;
056 import org.apache.axis2.jaxws.registry.FactoryRegistry;
057 import org.apache.axis2.jaxws.server.JAXWSMessageReceiver;
058 import org.apache.axis2.jaxws.server.endpoint.lifecycle.factory.EndpointLifecycleManagerFactory;
059 import org.apache.axis2.transport.OutTransportInfo;
060 import org.apache.axis2.transport.RequestResponseTransport;
061 import org.apache.axis2.transport.http.HTTPConstants;
062 import org.apache.axis2.transport.http.HTTPTransportReceiver;
063 import org.apache.axis2.transport.http.HTTPTransportUtils;
064 import org.apache.axis2.transport.http.TransportHeaders;
065 import org.apache.axis2.transport.http.util.RESTUtil;
066 import org.apache.axis2.util.MessageContextBuilder;
067 import org.apache.commons.logging.Log;
068 import org.apache.commons.logging.LogFactory;
069 import org.apache.geronimo.axis2.client.Axis2ConfigGBean;
070 import org.apache.geronimo.jaxws.JAXWSAnnotationProcessor;
071 import org.apache.geronimo.jaxws.JAXWSUtils;
072 import org.apache.geronimo.jaxws.JNDIResolver;
073 import org.apache.geronimo.jaxws.PortInfo;
074 import org.apache.geronimo.jaxws.ServerJNDIResolver;
075 import org.apache.geronimo.jaxws.annotations.AnnotationException;
076 import org.apache.geronimo.webservices.WebServiceContainer;
077 import org.apache.geronimo.webservices.saaj.SAAJUniverse;
078
079 /**
080 * @version $Rev$ $Date$
081 */
082 public abstract class Axis2WebServiceContainer implements WebServiceContainer {
083
084 private static final Log LOG = LogFactory.getLog(Axis2WebServiceContainer.class);
085
086 public static final String REQUEST = Axis2WebServiceContainer.class.getName() + "@Request";
087 public static final String RESPONSE = Axis2WebServiceContainer.class.getName() + "@Response";
088
089 private transient final ClassLoader classLoader;
090
091 protected String endpointClassName;
092 protected org.apache.geronimo.jaxws.PortInfo portInfo;
093 protected ConfigurationContext configurationContext;
094 protected JNDIResolver jndiResolver;
095 protected Class endpointClass;
096 protected AxisService service;
097 protected URL configurationBaseUrl;
098 protected WSDLQueryHandler wsdlQueryHandler;
099 protected Binding binding;
100 protected JAXWSAnnotationProcessor annotationProcessor;
101 protected Context context;
102 protected GeronimoFactoryRegistry factoryRegistry;
103
104 public Axis2WebServiceContainer(PortInfo portInfo,
105 String endpointClassName,
106 ClassLoader classLoader,
107 Context context,
108 URL configurationBaseUrl) {
109 this.classLoader = classLoader;
110 this.endpointClassName = endpointClassName;
111 this.portInfo = portInfo;
112 this.configurationBaseUrl = configurationBaseUrl;
113 this.context = context;
114 this.jndiResolver = new ServerJNDIResolver(context);
115 }
116
117 public void init() throws Exception {
118 this.endpointClass = classLoader.loadClass(this.endpointClassName);
119
120 Axis2ConfigGBean.registerClientConfigurationFactory();
121
122 configurationContext = ConfigurationContextFactory.createBasicConfigurationContext("META-INF/geronimo-axis2.xml");
123
124 // check to see if the wsdlLocation property is set in portInfo,
125 // if not checking if wsdlLocation exists in annotation
126 // if already set, annotation should not overwrite it.
127 if (portInfo.getWsdlFile() == null || portInfo.getWsdlFile().equals("")) {
128 // getwsdllocation from annotation if it exists
129 if (JAXWSUtils.containsWsdlLocation(this.endpointClass, classLoader)) {
130 portInfo.setWsdlFile(JAXWSUtils.getServiceWsdlLocation(this.endpointClass, classLoader));
131 }
132 }
133
134 AxisServiceGenerator serviceGen = createServiceGenerator();
135 if (portInfo.getWsdlFile() != null && !portInfo.getWsdlFile().equals("")) {
136 // WSDL file has been provided
137 service = serviceGen.getServiceFromWSDL(portInfo, endpointClass, configurationBaseUrl);
138 } else {
139 // No WSDL, let Axis2 handle it.
140 service = serviceGen.getServiceFromClass(this.endpointClass);
141 }
142
143 service.setScope(Constants.SCOPE_APPLICATION);
144 configurationContext.getAxisConfiguration().addService(service);
145
146 this.wsdlQueryHandler = new WSDLQueryHandler(this.service);
147
148 /*
149 * This replaces HandlerLifecycleManagerFactory for all web services.
150 * This should be ok as we do our own handler instance managment and injection.
151 * Also, this does not affect service-ref clients, as we install our own
152 * HandlerResolver.
153 */
154 FactoryRegistry.setFactory(HandlerLifecycleManagerFactory.class,
155 new GeronimoHandlerLifecycleManagerFactory());
156
157 FactoryRegistry.setFactory(EndpointLifecycleManagerFactory.class,
158 new GeronimoEndpointLifecycleManagerFactory());
159 }
160
161 protected AxisServiceGenerator createServiceGenerator() {
162 return new AxisServiceGenerator();
163 }
164
165 public void getWsdl(Request request, Response response) throws Exception {
166 doService(request, response);
167 }
168
169 public void invoke(Request request, Response response) throws Exception {
170 // set factory registry
171 GeronimoFactoryRegistry oldRegistry = GeronimoFactoryRegistry.getGeronimoFactoryRegistry();
172 GeronimoFactoryRegistry.setGeronimoFactoryRegistry(this.factoryRegistry);
173 // set saaj universe
174 SAAJUniverse universe = new SAAJUniverse();
175 universe.set(SAAJUniverse.AXIS2);
176 try {
177 doService(request, response);
178 } finally {
179 // unset saaj universe
180 universe.unset();
181 // unset factory registry
182 GeronimoFactoryRegistry.setGeronimoFactoryRegistry(oldRegistry);
183 }
184 }
185
186 protected void doService(final Request request, final Response response)
187 throws Exception {
188
189 if (LOG.isDebugEnabled()) {
190 LOG.debug("Target URI: " + request.getURI());
191 }
192
193 MessageContext msgContext = new MessageContext();
194 msgContext.setIncomingTransportName(Constants.TRANSPORT_HTTP);
195 msgContext.setProperty(MessageContext.REMOTE_ADDR, request.getRemoteAddr());
196
197 try {
198 TransportOutDescription transportOut = this.configurationContext.getAxisConfiguration()
199 .getTransportOut(Constants.TRANSPORT_HTTP);
200 TransportInDescription transportIn = this.configurationContext.getAxisConfiguration()
201 .getTransportIn(Constants.TRANSPORT_HTTP);
202
203 msgContext.setConfigurationContext(this.configurationContext);
204
205 //TODO: Port this segment for session support.
206 // String sessionKey = (String) this.httpcontext.getAttribute(HTTPConstants.COOKIE_STRING);
207 // if (this.configurationContext.getAxisConfiguration().isManageTransportSession()) {
208 // SessionContext sessionContext = this.sessionManager.getSessionContext(sessionKey);
209 // msgContext.setSessionContext(sessionContext);
210 // }
211 msgContext.setTransportIn(transportIn);
212 msgContext.setTransportOut(transportOut);
213 msgContext.setServiceGroupContextId(UUIDGenerator.getUUID());
214 msgContext.setServerSide(true);
215 msgContext.setAxisService(this.service);
216
217 doService2(request, response, msgContext);
218 } catch (AxisFault e) {
219 LOG.debug(e.getMessage(), e);
220 handleFault(msgContext, response, e);
221 } catch (Throwable e) {
222 String msg = "Exception occurred while trying to invoke service method doService()";
223 LOG.error(msg, e);
224 handleFault(msgContext, response, new AxisFault(msg, e));
225 }
226
227 }
228
229 private void handleFault(MessageContext msgContext, Response response, AxisFault e) {
230 // If the fault is not going along the back channel we should be 202ing
231 if (AddressingHelper.isFaultRedirected(msgContext)) {
232 response.setStatusCode(HttpURLConnection.HTTP_ACCEPTED);
233 } else {
234 response.setStatusCode(HttpURLConnection.HTTP_INTERNAL_ERROR);
235 }
236
237 msgContext.setProperty(MessageContext.TRANSPORT_OUT, response.getOutputStream());
238 msgContext.setProperty(Constants.OUT_TRANSPORT_INFO, new Axis2TransportInfo(response));
239
240 try {
241 MessageContext faultContext = MessageContextBuilder.createFaultMessageContext(msgContext, e);
242 AxisEngine.sendFault(faultContext);
243 } catch (Exception ex) {
244 LOG.warn("Error sending fault", ex);
245 if (!AddressingHelper.isFaultRedirected(msgContext)) {
246 response.setStatusCode(HttpURLConnection.HTTP_INTERNAL_ERROR);
247 response.setHeader(HTTPConstants.HEADER_CONTENT_TYPE, "text/plain");
248 PrintWriter pw = new PrintWriter(response.getOutputStream());
249 ex.printStackTrace(pw);
250 pw.flush();
251 }
252 }
253 }
254
255 protected String getServicePath(String contextRoot) {
256 String location = this.portInfo.getLocation();
257 if (location != null && location.startsWith(contextRoot)) {
258 return location.substring(contextRoot.length());
259 }
260 return null;
261 }
262
263 public static String trimContext(String contextPath) {
264 if (contextPath != null) {
265 if (contextPath.startsWith("/")) {
266 contextPath = contextPath.substring(1);
267 }
268 if (contextPath.endsWith("/")) {
269 contextPath = contextPath.substring(0, contextPath.length() - 1);
270 }
271 }
272 return contextPath;
273 }
274
275 public void doService2(Request request,
276 Response response,
277 MessageContext msgContext) throws Exception {
278
279 if (request.getMethod() == Request.GET) {
280 processGETRequest(request, response, this.service, msgContext);
281 } else if (request.getMethod() == Request.POST) {
282 processPOSTRequest(request, response, this.service, msgContext);
283 } else {
284 throw new UnsupportedOperationException("[" + request.getMethod() + " ] method not supported");
285 }
286
287 // Finalize response
288 OperationContext operationContext = msgContext.getOperationContext();
289 Object contextWritten = null;
290 Object isTwoChannel = null;
291 if (operationContext != null) {
292 contextWritten = operationContext.getProperty(Constants.RESPONSE_WRITTEN);
293 isTwoChannel = operationContext.getProperty(Constants.DIFFERENT_EPR);
294 }
295
296 if ((contextWritten != null) && Constants.VALUE_TRUE.equals(contextWritten)) {
297 if ((isTwoChannel != null) && Constants.VALUE_TRUE.equals(isTwoChannel)) {
298 response.setStatusCode(HttpURLConnection.HTTP_ACCEPTED);
299 return;
300 }
301 response.setStatusCode(HttpURLConnection.HTTP_OK);
302 } else {
303 response.setStatusCode(HttpURLConnection.HTTP_ACCEPTED);
304 }
305 }
306
307 public void destroy() {
308 }
309
310 public static class Axis2TransportInfo implements OutTransportInfo {
311 private Response response;
312
313 public Axis2TransportInfo(Response response) {
314 this.response = response;
315 }
316
317 public void setContentType(String contentType) {
318 response.setHeader(HTTPConstants.HEADER_CONTENT_TYPE, contentType);
319 }
320 }
321
322 protected void processGETRequest(Request request, Response response, AxisService service, MessageContext msgContext) throws Exception{
323 String query = request.getURI().getQuery();
324 if (query != null &&
325 (query.startsWith("wsdl") || query.startsWith("WSDL") ||
326 query.startsWith("xsd") || query.startsWith("XSD"))) {
327 // wsdl or xsd request
328
329 if (portInfo.getWsdlFile() != null && !portInfo.getWsdlFile().equals("")) {
330 URL wsdlURL = AxisServiceGenerator.getWsdlURL(portInfo.getWsdlFile(),
331 configurationBaseUrl,
332 classLoader);
333 this.wsdlQueryHandler.writeResponse(request.getURI().toString(),
334 wsdlURL.toString(),
335 response.getOutputStream());
336 } else {
337 throw new Exception("Service does not have WSDL");
338 }
339 } else if (AxisServiceGenerator.isSOAP11(service)) {
340 response.setContentType("text/html");
341 PrintWriter pw = new PrintWriter(response.getOutputStream());
342 pw.write("<html><title>Web Service</title><body>");
343 pw.write("Hi, this is '" + service.getName() + "' web service.");
344 pw.write("</body></html>");
345 pw.flush();
346 } else {
347 // REST request
348 processURLRequest(request, response, service, msgContext);
349 }
350 }
351
352 protected void processPOSTRequest(Request request, Response response, AxisService service, MessageContext msgContext) throws Exception {
353 processXMLRequest(request, response, service, msgContext);
354 }
355
356 protected void setMsgContextProperties(Request request, Response response, AxisService service, MessageContext msgContext) {
357 msgContext.setProperty(MessageContext.TRANSPORT_OUT, response.getOutputStream());
358 msgContext.setProperty(Constants.OUT_TRANSPORT_INFO, new Axis2TransportInfo(response));
359 msgContext.setProperty(RequestResponseTransport.TRANSPORT_CONTROL,
360 new Axis2RequestResponseTransport(response));
361 msgContext.setProperty(Constants.Configuration.TRANSPORT_IN_URL, request.getURI().toString());
362 msgContext.setIncomingTransportName(Constants.TRANSPORT_HTTP);
363
364 HttpServletRequest servletRequest =
365 (HttpServletRequest)request.getAttribute(WebServiceContainer.SERVLET_REQUEST);
366 msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST, servletRequest);
367
368 HttpServletResponse servletResponse =
369 (HttpServletResponse)request.getAttribute(WebServiceContainer.SERVLET_RESPONSE);
370 msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETRESPONSE, servletResponse);
371
372 ServletContext servletContext =
373 (ServletContext)request.getAttribute(WebServiceContainer.SERVLET_CONTEXT);
374 msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETCONTEXT, servletContext);
375
376 if (servletRequest != null) {
377 msgContext.setProperty(MessageContext.TRANSPORT_HEADERS,
378 new TransportHeaders(servletRequest));
379 }
380
381 if (this.binding != null) {
382 msgContext.setProperty(JAXWSMessageReceiver.PARAM_BINDING, this.binding);
383 }
384
385 msgContext.setTo(new EndpointReference(request.getURI().toString()));
386 }
387
388 protected void processXMLRequest(Request request,
389 Response response,
390 AxisService service,
391 MessageContext msgContext) throws Exception {
392 String contentType = request.getHeader(HTTPConstants.HEADER_CONTENT_TYPE);
393 String soapAction = request.getHeader(HTTPConstants.HEADER_SOAP_ACTION);
394 if (soapAction == null) {
395 soapAction = "\"\"";
396 }
397
398 ConfigurationContext configurationContext = msgContext.getConfigurationContext();
399 configurationContext.fillServiceContextAndServiceGroupContext(msgContext);
400
401 setMsgContextProperties(request, response, service, msgContext);
402
403 if (!HTTPTransportUtils.isRESTRequest(contentType)) {
404 HTTPTransportUtils.processHTTPPostRequest(msgContext,
405 request.getInputStream(),
406 response.getOutputStream(),
407 contentType,
408 soapAction,
409 request.getURI().getPath());
410 } else {
411 RESTUtil.processXMLRequest(msgContext,
412 request.getInputStream(),
413 response.getOutputStream(),
414 contentType);
415 }
416 }
417
418 protected void processURLRequest(Request request,
419 Response response,
420 AxisService service,
421 MessageContext msgContext) throws Exception {
422 String contentType = request.getHeader(HTTPConstants.HEADER_CONTENT_TYPE);
423
424 ConfigurationContext configurationContext = msgContext.getConfigurationContext();
425 configurationContext.fillServiceContextAndServiceGroupContext(msgContext);
426
427 setMsgContextProperties(request, response, service, msgContext);
428
429 InvocationResponse processed = RESTUtil.processURLRequest(msgContext,
430 response.getOutputStream(),
431 contentType);
432
433 if (!processed.equals(InvocationResponse.CONTINUE)) {
434 response.setStatusCode(HttpURLConnection.HTTP_OK);
435 String s = HTTPTransportReceiver.getServicesHTML(configurationContext);
436 PrintWriter pw = new PrintWriter(response.getOutputStream());
437 pw.write(s);
438 pw.flush();
439 }
440 }
441
442 /*
443 * Gets the right handlers for the port/service/bindings and performs injection.
444 */
445 protected void configureHandlers() throws Exception {
446 EndpointDescription desc = AxisServiceGenerator.getEndpointDescription(this.service);
447 if (desc == null) {
448 this.binding = new BindingImpl("");
449 } else {
450 String xml = this.portInfo.getHandlersAsXML();
451 HandlerChainsType handlerChains = null;
452 if (xml != null) {
453 ByteArrayInputStream in = new ByteArrayInputStream(xml.getBytes("UTF-8"));
454 handlerChains = DescriptionUtils.loadHandlerChains(in);
455 desc.setHandlerChain(handlerChains);
456 }
457
458 if (LOG.isDebugEnabled()) {
459 logHandlers(desc.getHandlerChain());
460 }
461
462 this.binding = BindingUtils.createBinding(desc);
463
464 DescriptionUtils.registerHandlerHeaders(desc.getAxisService(), this.binding.getHandlerChain());
465 }
466 }
467
468 private void logHandlers(HandlerChainsType handlerChains) {
469 if (handlerChains == null || handlerChains.getHandlerChain() == null
470 || handlerChains.getHandlerChain().isEmpty()) {
471 LOG.debug("No handlers");
472 return;
473 }
474
475 for (HandlerChainType chains : handlerChains.getHandlerChain()) {
476 LOG.debug("Handler chain: " + chains.getServiceNamePattern() + " " +
477 chains.getPortNamePattern() + " " + chains.getProtocolBindings());
478 if (chains.getHandler() != null) {
479 for (HandlerType chain : chains.getHandler()) {
480 LOG.debug(" Handler: " + chain.getHandlerName().getValue() + " " +
481 chain.getHandlerClass().getValue());
482 }
483 }
484 }
485 }
486
487 protected void injectHandlers() {
488 List<Handler> handlers = this.binding.getHandlerChain();
489 try {
490 for (Handler handler : handlers) {
491 injectResources(handler);
492 }
493 } catch (AnnotationException e) {
494 throw new WebServiceException("Handler annotation failed", e);
495 }
496 }
497
498 protected void destroyHandlers() {
499 if (this.annotationProcessor != null) {
500 // call handlers preDestroy
501 List<Handler> handlers = this.binding.getHandlerChain();
502 for (Handler handler : handlers) {
503 this.annotationProcessor.invokePreDestroy(handler);
504 }
505 }
506 }
507
508 protected void injectResources(Object instance) throws AnnotationException {
509 this.annotationProcessor.processAnnotations(instance);
510 this.annotationProcessor.invokePostConstruct(instance);
511 }
512
513 }