Web Services from a Spring Enabled Web-App WebSpringServletApacheBean
程序员文章站
2022-06-17 15:46:46
...
I spent some time recently working on an web-application using spring and webwork. (<tangent>this combination created the core of one of the cleanest architectures I've worked with</tangent>). Someone asked how easy it might be to expose some functionality as a web service, so I thought I'd give it a go.
Grabbing a copy of Apache Axis was my first step. It's documentation guided me most of the way, but the only missing bit was how to get at a spring-created bean from an axis-aware class. Here's what I did.
After putting the required libraries (axis-ant.jar, axis.jar, commons-discovery.jar, commons-loggin.jar, jaxrpc.jar, wsdl4j.jar, saaj.jar) on the classpath, I had to declare all of the appropriate axis stuff in my web.xml file.
<web-app>
<listener>
<listener-class>org.apache.axis.transport.http.AxisHTTPSessionListener</listener-class>
</listener>
<servlet>
<servlet-name>axis</servlet-name>
<servlet-class>org.apache.axis.transport.http.AxisServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>axis</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
<mime-mapping>
<extension>wsdl</extension>
<mime-type>text/xml</mime-type>
</mime-mapping>
</web-app>
The next step was to create a class which would be exposed as a web service by Axis:
public class TodoItemWebService {
public String getTodoItemDescription(String title) {
return "dummy description";
}
}
To get the service to run, I also had to create a file named server-config.wsdd in the WEB-INF directory of my web application. I figured out what this looked like by going through the Axis tutorial which dynamically registers a service in your app. Statically creating the file works fine though too. Here's what mine looked like:
<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<handler name="LocalResponder" type="java:org.apache.axis.transport.local.LocalResponder"/>
<handler name="URLMapper" type="java:org.apache.axis.handlers.http.URLMapper"/>
<handler name="Authenticate" type="java:org.apache.axis.handlers.SimpleAuthenticationHandler"/>
<service name="items" provider="java:RPC" style="wrapped" use="literal">
<operation name="getTodoItemDescription" qname="ns1:getTodoList" returnQName="ns1:getTodoItemDescriptionResult"
returnType="xsd:string" soapAction="" xmlns:ns1="http://wrytradesman.com/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<parameter qname="ns1:TodoItemTitle" type="xsd:string"/>
</operation>
<parameter name="allowedMethods" value="*"/>
<parameter name="className" value="com.wrytradesman.todolist.web.service.TodoItemWebService"/>
<parameter name="wsdlTargetNamespace" value="http://wrytradesman.com/"/>
</service>
<transport name="http">
<requestFlow>
<handler type="URLMapper"/>
<handler type="java:org.apache.axis.handlers.http.HTTPAuthHandler"/>
</requestFlow>
<parameter name="qs:list" value="org.apache.axis.transport.http.QSListHandler"/>
<parameter name="qs:wsdl" value="org.apache.axis.transport.http.QSWSDLHandler"/>
<parameter name="qs.list" value="org.apache.axis.transport.http.QSListHandler"/>
<parameter name="qs.method" value="org.apache.axis.transport.http.QSMethodHandler"/>
<parameter name="qs:method" value="org.apache.axis.transport.http.QSMethodHandler"/>
<parameter name="qs.wsdl" value="org.apache.axis.transport.http.QSWSDLHandler"/>
</transport>
<transport name="local">
<responseFlow>
<handler type="LocalResponder"/>
</responseFlow>
</transport>
</deployment>
The service definition in bold is what I had to write to get the service running. All of the surrounding info is setup stuff that is just needed to configure axis. At this stage of the game, I could deploy my web application and hit a url http://localhost//services/items?wsdl to get the WSDL definition for my service. In the space of about 5 minutes, a cow-orker had used that WSDL to create a .NET based client which called the service and got my hard-coded response.
With my web service now up and running, the only remaining problem was to create a "real" implementation that used one of my spring managed objects instead of a hard-coded response. The trick here is to get a reference to the application context first. Everything from that point is easy. The simplest path to that is get a reference to the servlet from the Axis message context, then use the servlet context to get the application context from spring. Then just call your spring bean as per normal. Here's what my final code looks like:
public class TodoItemWebService {
public String getTodoItemDescription(String title) {
HttpServlet servlet = (HttpServlet) MessageContext.getCurrentContext().getProperty(
HTTPConstants.MC_HTTP_SERVLET);
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servlet.getServletContext());
Map beans = context.getBeansOfType(TodoListRepository.class, false, false);
Iterator iterator = beans.values().iterator();
TodoListRepository repository = (TodoListRepository) iterator.next();
TodoItem item = repository.getTodoList().getItem(title);
return item.getDescription();
}
}
Thats it. Pretty straight forward really. There's a few corner cases to be covered off, but vertical slice is complete. The whole exercise took less than a days effort, which included learning about how Axis hangs together.
Posted by marty at December 14, 2004 10:42 AM [0 Trackbacks]
Comments
Another option is to extend org.springframework.remoting.jaxrpc.ServletEndpointSupport.
I'm using this to insert a level of indirection, where the Axis-registered service class delegates to a bean it retrieves from the application context.
import org.springframework.context.ApplicationContext;
import org.springframework.remoting.jaxrpc.ServletEndpointSupport;
public class JaxRpcVerzeichnisService extends ServletEndpointSupport
implements RemoteMyService {
private MyService _service;
protected void onInit() {
ApplicationContext ac = getWebApplicationContext();
assert ac != null : "no ApplicationContext available";
_service = (MyService)ac.getBean("myservice");
}
public Object myServiceMethod() {
assert _service != null : "_service not initialized";
return _service.myServiceMethod();
}
}
Posted by: Michael Schuerig at December 14, 2004 08:50 PM
THANK YOU! Man I started looking at this last Friday and was dreading the fight... you've made it easy. Thanks VERY VERY much!
R
Posted by: Robert S. Sfeir at December 15, 2004 01:24 AM
you might also take a look at:
http://opensource.atlassian.com/projects/spring/browse/SPR-371
Posted by: The VoodooChile at December 15, 2004 11:53 PM
Interessting options. It would be really helpful to have an official route documented in the Spring docs but i cant find any Webservice-Section at all.
The proposed url of "The VoodooChile" is quite promising though and i think i will take this route. But great blog on this topic, i appreciated the reading.
Marc
Posted by: Marc Logemann at January 27, 2005 01:09 AM
This code doesn't work- specifically
1. what's TodoItem?
2. what's TodoListRepository?
Appreciate your response.
Thx.
lkj
ublic class TodoItemWebService {
public String getTodoItemDescription(String title) {
HttpServlet servlet = (HttpServlet) MessageContext.getCurrentContext().getProperty(
HTTPConstants.MC_HTTP_SERVLET);
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servlet.getServletContext());
Map beans = context.getBeansOfType(TodoListRepository.class, false, false);
Iterator iterator = beans.values().iterator();
TodoListRepository repository = (TodoListRepository) iterator.next();
TodoItem item = repository.getTodoList().getItem(title);
return item.getDescription();
}
}
Posted by: lalit at January 27, 2005 09:49 AM
Grabbing a copy of Apache Axis was my first step. It's documentation guided me most of the way, but the only missing bit was how to get at a spring-created bean from an axis-aware class. Here's what I did.
After putting the required libraries (axis-ant.jar, axis.jar, commons-discovery.jar, commons-loggin.jar, jaxrpc.jar, wsdl4j.jar, saaj.jar) on the classpath, I had to declare all of the appropriate axis stuff in my web.xml file.
<web-app>
<listener>
<listener-class>org.apache.axis.transport.http.AxisHTTPSessionListener</listener-class>
</listener>
<servlet>
<servlet-name>axis</servlet-name>
<servlet-class>org.apache.axis.transport.http.AxisServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>axis</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
<mime-mapping>
<extension>wsdl</extension>
<mime-type>text/xml</mime-type>
</mime-mapping>
</web-app>
The next step was to create a class which would be exposed as a web service by Axis:
public class TodoItemWebService {
public String getTodoItemDescription(String title) {
return "dummy description";
}
}
To get the service to run, I also had to create a file named server-config.wsdd in the WEB-INF directory of my web application. I figured out what this looked like by going through the Axis tutorial which dynamically registers a service in your app. Statically creating the file works fine though too. Here's what mine looked like:
<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<handler name="LocalResponder" type="java:org.apache.axis.transport.local.LocalResponder"/>
<handler name="URLMapper" type="java:org.apache.axis.handlers.http.URLMapper"/>
<handler name="Authenticate" type="java:org.apache.axis.handlers.SimpleAuthenticationHandler"/>
<service name="items" provider="java:RPC" style="wrapped" use="literal">
<operation name="getTodoItemDescription" qname="ns1:getTodoList" returnQName="ns1:getTodoItemDescriptionResult"
returnType="xsd:string" soapAction="" xmlns:ns1="http://wrytradesman.com/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<parameter qname="ns1:TodoItemTitle" type="xsd:string"/>
</operation>
<parameter name="allowedMethods" value="*"/>
<parameter name="className" value="com.wrytradesman.todolist.web.service.TodoItemWebService"/>
<parameter name="wsdlTargetNamespace" value="http://wrytradesman.com/"/>
</service>
<transport name="http">
<requestFlow>
<handler type="URLMapper"/>
<handler type="java:org.apache.axis.handlers.http.HTTPAuthHandler"/>
</requestFlow>
<parameter name="qs:list" value="org.apache.axis.transport.http.QSListHandler"/>
<parameter name="qs:wsdl" value="org.apache.axis.transport.http.QSWSDLHandler"/>
<parameter name="qs.list" value="org.apache.axis.transport.http.QSListHandler"/>
<parameter name="qs.method" value="org.apache.axis.transport.http.QSMethodHandler"/>
<parameter name="qs:method" value="org.apache.axis.transport.http.QSMethodHandler"/>
<parameter name="qs.wsdl" value="org.apache.axis.transport.http.QSWSDLHandler"/>
</transport>
<transport name="local">
<responseFlow>
<handler type="LocalResponder"/>
</responseFlow>
</transport>
</deployment>
The service definition in bold is what I had to write to get the service running. All of the surrounding info is setup stuff that is just needed to configure axis. At this stage of the game, I could deploy my web application and hit a url http://localhost//services/items?wsdl to get the WSDL definition for my service. In the space of about 5 minutes, a cow-orker had used that WSDL to create a .NET based client which called the service and got my hard-coded response.
With my web service now up and running, the only remaining problem was to create a "real" implementation that used one of my spring managed objects instead of a hard-coded response. The trick here is to get a reference to the application context first. Everything from that point is easy. The simplest path to that is get a reference to the servlet from the Axis message context, then use the servlet context to get the application context from spring. Then just call your spring bean as per normal. Here's what my final code looks like:
public class TodoItemWebService {
public String getTodoItemDescription(String title) {
HttpServlet servlet = (HttpServlet) MessageContext.getCurrentContext().getProperty(
HTTPConstants.MC_HTTP_SERVLET);
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servlet.getServletContext());
Map beans = context.getBeansOfType(TodoListRepository.class, false, false);
Iterator iterator = beans.values().iterator();
TodoListRepository repository = (TodoListRepository) iterator.next();
TodoItem item = repository.getTodoList().getItem(title);
return item.getDescription();
}
}
Thats it. Pretty straight forward really. There's a few corner cases to be covered off, but vertical slice is complete. The whole exercise took less than a days effort, which included learning about how Axis hangs together.
Posted by marty at December 14, 2004 10:42 AM [0 Trackbacks]
Comments
Another option is to extend org.springframework.remoting.jaxrpc.ServletEndpointSupport.
I'm using this to insert a level of indirection, where the Axis-registered service class delegates to a bean it retrieves from the application context.
import org.springframework.context.ApplicationContext;
import org.springframework.remoting.jaxrpc.ServletEndpointSupport;
public class JaxRpcVerzeichnisService extends ServletEndpointSupport
implements RemoteMyService {
private MyService _service;
protected void onInit() {
ApplicationContext ac = getWebApplicationContext();
assert ac != null : "no ApplicationContext available";
_service = (MyService)ac.getBean("myservice");
}
public Object myServiceMethod() {
assert _service != null : "_service not initialized";
return _service.myServiceMethod();
}
}
Posted by: Michael Schuerig at December 14, 2004 08:50 PM
THANK YOU! Man I started looking at this last Friday and was dreading the fight... you've made it easy. Thanks VERY VERY much!
R
Posted by: Robert S. Sfeir at December 15, 2004 01:24 AM
you might also take a look at:
http://opensource.atlassian.com/projects/spring/browse/SPR-371
Posted by: The VoodooChile at December 15, 2004 11:53 PM
Interessting options. It would be really helpful to have an official route documented in the Spring docs but i cant find any Webservice-Section at all.
The proposed url of "The VoodooChile" is quite promising though and i think i will take this route. But great blog on this topic, i appreciated the reading.
Marc
Posted by: Marc Logemann at January 27, 2005 01:09 AM
This code doesn't work- specifically
1. what's TodoItem?
2. what's TodoListRepository?
Appreciate your response.
Thx.
lkj
ublic class TodoItemWebService {
public String getTodoItemDescription(String title) {
HttpServlet servlet = (HttpServlet) MessageContext.getCurrentContext().getProperty(
HTTPConstants.MC_HTTP_SERVLET);
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servlet.getServletContext());
Map beans = context.getBeansOfType(TodoListRepository.class, false, false);
Iterator iterator = beans.values().iterator();
TodoListRepository repository = (TodoListRepository) iterator.next();
TodoItem item = repository.getTodoList().getItem(title);
return item.getDescription();
}
}
Posted by: lalit at January 27, 2005 09:49 AM
上一篇: 做一个简单的回帖系统的思路是什么?我这样的想法对不对?
下一篇: THINKPHP框架代码_个人修复