Webservices with HttpInvoker

It's easy to export the AppFuse managers as a webservice to various clients using Spring remoting.

The HttpInvoker

If you only have Java clients, the easiest solution for exporting managers is using the HttpInvoker. As it uses Java serialization, you don't have to care about object mappings or conversions. Additionally, by using HTTP as a protocol, it's trivial to allow access to the services via a proxy. If you later on wish to change the protocol (e.g. to allow non-Java clients to access your services), Spring makes it easy to replace the remoting protocol by only changing one line in a config file.

Create a servlet

The managers will get exported using a servlet. Therefore you have to create two new files. The first one's purpose is to provide managers without access control. This is needed to allow clients to authenticate themselves. The second file contains managers, that get secured using Acegi, so only authenticated clients are able to use them.

src/main/webapp/WEB-INF/ws-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
 
	<bean name="/RemoteAuthenticationManager" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
		<property name="service" ref="remoteAuthenticationManager"/>
		<property name="serviceInterface" value="org.acegisecurity.providers.rcp.RemoteAuthenticationManager"/>
	</bean>
 
</beans>

src/main/webapp/WEB-INF/ws-secure-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
 
	<bean name="/UserManager" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
		<property name="service" ref="userManager"/>
		<property name="serviceInterface" value="org.appfuse.service.UserManager"/>
	</bean>
 
	<bean name="/RoleManager" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
		<property name="service" ref="roleManager"/>
		<property name="serviceInterface" value="org.appfuse.service.RoleManager"/>
	</bean>
 
</beans>

Security settings

Now we have to modify the security settings, to secure our ws-secure-Servlet.

src/main/webapp/WEB-INF/security.xml

We have to add some additional beans to this configuration file:

<!-- Filter with out session creaton for webservices -->
<bean id="httpSessionContextIntegrationFilterWithASCFalse"
	class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">
	<property name="allowSessionCreation" value="false" />
</bean>
 
<!-- Auth-Filter for beans exposed as webservices -->
<bean id="basicProcessingFilter"
	class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
	<property name="authenticationManager">
		<ref bean="authenticationManager" />
	</property>
	<property name="authenticationEntryPoint">
		<ref bean="basicProcessingFilterEntryPoint" />
	</property>
</bean>
 
<bean id="basicProcessingFilterEntryPoint"
	class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
	<property name="realmName">
		<value>zCRM webservices</value>
	</property>
</bean>
 
<!-- Allows remote clients to check if a username/password is valid -->
<bean id="remoteAuthenticationManager"
	class="org.acegisecurity.providers.rcp.RemoteAuthenticationManagerImpl">
	<property name="authenticationManager">
		<ref bean="authenticationManager" />
	</property>
</bean>

The next thing is to rename the bean called httpSessionContextIntegrationFilter in httpSessionContextIntegrationFilterWithASCTrue (it's not necessary by any means, but will help us to keep a better overview).

Finally we have to modify the bean filterChainProxy, so that the last two lines look like this:

/ws/**=httpSessionContextIntegrationFilterWithASCFalse,basicProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
/**=httpSessionContextIntegrationFilterWithASCTrue,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor

As you see, the services don't need all filters of the web-layer, plus we turned off session creation for the ws-subdiretory, as remoting clients don't need this.

Register the servlets

The servlets now have to be registered, so they'll be avilable to our clients. Because we therefore have to add some defintions into web.xml, we need to run mvn war:inplace (see AppFuse Quickstart Guide). You can safely remove all newly created files other than web.xml.

src/main/webapp/WEB-INF/web.xml

We define a new filter mapping for the secured services:

<filter-mapping>
	<filter-name>securityFilter</filter-name>
	<url-pattern>/ws/secure/*</url-pattern>
</filter-mapping>

Then the servlets need to be defined and the load order has to be modified:

<servlet>
	<servlet-name>ws-secure</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<load-on-startup>10</load-on-startup>
</servlet>
 
<servlet>
	<servlet-name>ws</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<load-on-startup>20</load-on-startup>
</servlet>
 
<servlet>
	<servlet-name>dispatcher</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<load-on-startup>30</load-on-startup>
</servlet>

Last thing left is to add the servlet mappings:

<servlet-mapping>
	<servlet-name>ws-secure</servlet-name>
	<url-pattern>/ws/secure/*</url-pattern>
</servlet-mapping>
 
<servlet-mapping>
	<servlet-name>ws</servlet-name>
	<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
Recent changes RSS feed Creative Commons License Donate Driven by DokuWiki