payload source of response with spring ws MockWebServiceClient - integration-testing

I'm using spring-ws 2.0.2 and spring-ws-test to run integration tests of my SOAP server. I'm using an approach exactly like that from http://static.springsource.org/spring-ws/site/apidocs/org/springframework/ws/test/server/MockWebServiceClient.html
Here's the code I'm running with the expected response XML omitted for brevity and because it's not relevant to the question.
I'd like to be able to see the xml in the response payload but can't figure out how to get access to it. If I set a breakpoint after response is set and inspect it I can see that it has a private messageContext.response of type SaajSoapMessage but I can't figure out how to access it or if there's a better way to see the response XML.
package com.example.integration;
import static org.springframework.ws.test.server.RequestCreators.*;
import static org.springframework.ws.test.server.ResponseMatchers.*;
import static org.testng.AssertJUnit.*;
import javax.xml.transform.Source;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.ws.test.server.MockWebServiceClient;
import org.springframework.ws.test.server.ResponseActions;
import org.springframework.xml.transform.StringSource;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
#ContextConfiguration(locations={"/transaction-test-context.xml", "/spring-web-services-servlet.xml"})
public class BaseWebServiceIntegrationTest {
#Autowired
private ApplicationContext applicationContext;
private MockWebServiceClient mockClient;
#BeforeClass(groups="integration")
public void createClient() {
assertNotNull("expected applicationContext to be non-null", applicationContext);
mockClient = MockWebServiceClient.createClient(applicationContext);
}
#Test(groups="integration")
public void proofOfConcept() {
assertNotNull("expected mockClient to be non-null", mockClient);
Source requestPayload = new StringSource(
"<s0:ListDevicesRequest " +
"xmlns:soapenc='http://schemas.xmlsoap.org/soap/encoding/' " +
"xmlns:xs='http://www.w3.org/2001/XMLSchema' " +
"xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' " +
"xmlns:s0='urn:com:example:xmlschema:service:v1.1:DeviceManager.xsd'>" +
"<s0:MacID>66:bb:be:ee:00:00:01:00</s0:MacID>" +
"</s0:ListDevicesRequest>"
);
Source responsePayload = new StringSource("<!-- response omitted for the post, not relevant -->");
ResponseActions response = mockClient.sendRequest(withPayload(requestPayload));
response.andExpect(noFault())
.andExpect(payload(responsePayload));
}
}
edited to add the application context file as requested. we have a few but the one below is the soap-specific one.
<?xml version="1.0"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<sec:global-method-security pre-post-annotations="disabled" jsr250-annotations="disabled" />
<tx:annotation-driven/>
<context:component-scan base-package="com.example.integration"/>
<context:component-scan base-package="com.example.signal"/>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
<bean id="faultService" class="com.example.webservice.fault.FaultService"/>
<bean id="soapDeviceService" class="com.example.webservice.device.SoapDeviceServiceImpl"/>
<!-- 1.1 Endpoints -->
<bean id="deviceManagerEndpoint_v1_1" class="com.example.webservice.spring.DeviceManagerEndpoint">
<property name="soapDeviceService" ref="soapDeviceService"/>
</bean>
<bean class="com.example.webservice.spring.PayloadMethodMarshallingEndpointAdapter">
<property name="marshaller" ref="marshaller"/>
<property name="unmarshaller" ref="marshaller"/>
</bean>
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="mtomEnabled" value="false"/>
<property name="contextPath" value="com.example.xmlschema.service.v1_0.devicemanager"/>
</bean>
<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
<property name="interceptors">
<list>
<bean class="com.example.webservice.interceptor.LoggingInterceptor"/>
<ref bean="validatingInterceptor"/>
</list>
</property>
</bean>
<bean id="validatingInterceptor" class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
<property name="schemas">
<list>
<value>/WEB-INF/wsdl/*Types_v*.xsd</value>
</list>
</property>
<property name="validateRequest" value="true"/>
<property name="validateResponse" value="true"/>
</bean>
<bean class="com.example.webservice.spring.SpringWebserviceFaultMappingExceptionResolver">
<property name="order" value="1"/>
<property name="marshaller" ref="marshaller"/>
</bean>
</beans>

Ok, 4 years too late, but here is my answer, which I am both proud and ashamed of :-
.andExpect(
new ResponseMatcher()
{
#Override
public void match(WebServiceMessage request, WebServiceMessage response)
throws IOException, AssertionError
{
response.writeTo(System.out);
}
})
And since Java 8:
.andExpect((request, response) -> response.writeTo(System.out))

You could install a PayloadLoggingInterceptor in your spring-ws context which will by default log all request and response payloads. Configure it like this:
<sws:interceptors>
<bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
</sws:interceptors>
Check out the springsource docs for more info
here

Configure log4j and add configuration in log4j to show the spring classes logs. The logs will show the order in which spring classes are called and also the request and response sent.
<logger name="org.springframework.ws" additivity="false">
<level value="DEBUG#org.tiaa.infra.logging.TiaaLevel" />
<appender-ref ref="DailyRollingFileAppender"/>
</logger>

+1 for #NeilStevens solution... It's never too late!. Thanks :-) Here is a slight refactor to print stuff out to a sf4j logger and use the magic of static imports...:
import static SomeTestClass.ResponseOutputMatcher.printResponse;
public class SomeTestClass {
private static final Logger LOG = LoggerFactory.getLogger(SomeTestClass.class);
#Test
public void someTest() {
mockClient.sendRequest(...)
.andExpect(printResponse(LOG))
.andExpect(noFault());
}
#RequiredArgsConstructor
public static class ResponseOutputMatcher implements ResponseMatcher {
private final Logger logger;
public ResponseOutputMatcher() {
this(LoggerFactory.getLogger(ResponseOutputMatcher.class));
}
#Override
public void match(WebServiceMessage request, WebServiceMessage response) throws IOException, AssertionError {
ByteArrayOutputStream out = new ByteArrayOutputStream();
response.writeTo(out);
logger.info(format("Received payload response:\n%s\n%s\n%s", repeat(">", 80), out.toString(), repeat("_", 80)));
}
public static ResponseMatcher printResponse(Logger logger) {
return new ResponseOutputMatcher(logger);
}
}

Related

spring data redis message listener listening messages twice

I am trying to use org.springframework.data.redis.listener.RedisMessageListenerContainer to listen on the redis pubsub channel.
my MessageListener class looks like this
#Component
public class RedisMessageListener {
#Autowired
private ProcessAdapterImpl processAdapter;
private static Logger logger = LoggerFactory.getLogger(RedisMessageListener.class);
public void handleMessage(Serializable message, String channel) {
logger.debug("handleMessage >>>>> Message received: " + message.toString() + " channel:" + channel);
processAdapter.onEventReceived(message.toString());
}
}
and in application context
<bean id="tbProperties"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:conf/notifier-local.properties
</value>
</property>
</bean>
<bean id="jedisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.server}" />
<property name="port" value="${redis.port}" />
<property name="usePool" value="true" />
</bean>
<!-- redis template definition -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate"
p:connection-factory-ref="jedisConnectionFactory" />
<bean id="redisPublisher"
class="com.vgroup.apps.notifier.event.publisher.RedisPublisherImpl" />
<bean id="messageListener"
class="org.springframework.data.redis.listener.adapter.MessageListenerAdapter">
<constructor-arg>
<bean class="com.vgroup.apps.notifier.event.listener.RedisMessageListener" />
</constructor-arg>
</bean>
<bean id="redisContainer"
class="org.springframework.data.redis.listener.RedisMessageListenerContainer">
<property name="connectionFactory" ref="jedisConnectionFactory" />
<property name="messageListeners">
<map>
<entry key-ref="messageListener">
<bean class="org.springframework.data.redis.listener.ChannelTopic">
<constructor-arg value="${redis.queue}" />
</bean>
</entry>
</map>
</property>
</bean>
redis publisher looks like below
#Component
public class RedisPublisherImpl implements IRedisPublisher {
#Autowired
private StringRedisTemplate redisTemplate;
#Autowired
private ObjectMapper mapper;
private static Logger logger = LoggerFactory.getLogger(RedisPublisherImpl.class);
public void afterPropertiesSet() throws Exception {
}
#Override
public void publish(Object event) {
logger.info("Start executing publish object");
try {
String jsonString = mapper.writeValueAsString(event);
redisTemplate.convertAndSend("notifications", jsonString);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
logger.info("End executing publish object");
}
}
with this configuration, for some strange reasons I am getting a message twice (some times even 3-4 times) even if it is sent by my publisher only once
in my logs I can see that it is message is being listened by 2 different rediscontainer threads
2015-12-03 09:13:12.152 [redisContainer-2] DEBUG c.v.a.n.e.l.RedisMessageListener - handleMessage >>>>> Message received: abc channel:notifications
2015-12-03 09:13:12.152 [redisContainer-2] DEBUG c.v.a.n.e.l.RedisMessageListener - handleMessage >>>>> Message received: def channel:notifications
2015-12-03 09:13:12.156 [redisContainer-3] DEBUG c.v.a.n.e.l.RedisMessageListener - handleMessage >>>>> Message received: abc channel:notifications
2015-12-03 09:13:12.157 [redisContainer-3] DEBUG c.v.a.n.e.l.RedisMessageListener - handleMessage >>>>> Message received: def channel:notifications
Check if the beans are creating twice (I.e the subscription is happening twice). I had similar problem, in web.xml. I had both contextListener and DispatchServlet which is initializing the beans twice there by subscribing twice.

How to convert from spring-integration-kafka 1.0.0M (XML config) to spring-integration-kafka 1.2.1 (Java config)?

I implemented spring-integration-kafka 1.0.0M into a Spring MVC project last year using the XML configuration and it was very simple to do. Since Spring seems to be moving in a Java configuration direction (rather than XML), I would like to convert from using the spring-integration-kafka XML configuration to the Java configuration, which the latest version of spring-integration-kafka (1.2.1) supports. The problem is that there really aren't many complete examples of this being done online, and the examples I have found look to be out-of-date from what I can tell. The configuration I have is pretty simple:
<bean id="kafkaStringEncoder" class="org.springframework.integration.kafka.serializer.common.StringEncoder" />
<bean id="customObjectMapper" class="ad.content.api.utils.ObjectMapperFactory" factory-method="getMapper" />
<int:channel id="kafkaConversionRequest" />
<bean id="producerProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="message.send.max.retries">${kafka.retries}</prop>
</props>
</property>
</bean>
<int-kafka:producer-context id="kafkaWidgetProducerContext" producer-properties="producerProperties">
<int-kafka:producer-configurations>
<int-kafka:producer-configuration
broker-list="${kafka.broker}" key-class-type="java.lang.String"
key-encoder="kafkaStringEncoder" value-class-type="java.lang.String"
value-encoder="kafkaStringEncoder" topic="widget-.*"
compression-codec="default" async="true" />
</int-kafka:producer-configurations>
</int-kafka:producer-context>
<!-- declare spring integration gateway for kafka -->
<int:gateway service-interface="ad.content.api.models.kafka.KafkaGateway" default-reply-timeout="2000">
<int:method name="publishConversion" request-channel="kafkaConversionRequest" />
</int:gateway>
<int:chain input-channel="kafkaConversionRequest" output-channel="kafkaToJson">
<int:header-enricher>
<int:header name="topic" value="widget-conversion" />
</int:header-enricher>
</int:chain>
<int:object-to-json-transformer input-channel="kafkaToJson" output-channel="kafkaOutbound" object-mapper="customObjectMapper" />
<int-kafka:outbound-channel-adapter id="kafkaOutbound" kafka-producer-context-ref="kafkaWidgetProducerContext" />
Here's what I can figure out so far:
// gateway
#MessagingGateway(defaultReplyTimeout="2000")
public interface KafkaGateway {
#Gateway(requestChannel="kafkaConversionRequest", headers=#GatewayHeader(name="topic", value="widget-conversion"))
void publishConversion(Conversion conversion);
}
// create channel
#Bean(name="kafkaConversionRequest")
public MessageChannel getConversionRequest() {
return new DirectChannel();
}
#Bean
public KafkaProducerMessageHandler getHandler() throws Exception {
return new KafkaProducerMessageHandler(getContext());
}
#Bean
public KafkaProducerContext getContext() throws Exception {
KafkaProducerContext context = new KafkaProducerContext();
context.setProducerConfigurations(Collections.singletonMap("config", getConfiguration()));
return context;
}
#Bean
public ProducerConfiguration<String, String> getConfiguration() throws Exception {
return new ProducerConfiguration<String, String>(getMetaData(), getProducer());
}
#Bean
#Transformer(inputChannel="kafkaToJson", outputChannel="kafkaOutbound")
public ObjectToJsonTransformer getJsonTransformer() {
return new ObjectToJsonTransformer();
}
#Bean
public ProducerMetadata<String, String> getMetaData() {
StringSerializer serializer = new StringSerializer();
return new ProducerMetadata<String, String>("widget-.*", String.class, String.class, serializer, serializer);
}
#Bean
public Producer<String, String> getProducer() throws Exception {
return new ProducerFactoryBean<String, String>(getMetaData(), "dev.kafka-broker01:9092").getObject();
}
There's an outstanding pull request for a kafka sample that might help you.
Here is a fully functional example of configuring Kafka using Spring Java Config and equivalent XML version as well: https://spring.io/blog/2015/04/15/using-apache-kafka-for-integration-and-data-processing-pipelines-with-spring

Create Two Factor Authentication in Spring MVC

Currently our webapps use Spring Security (Acegi Security 1.0.3) with username and password authentication. I have recently been given the task of plugging in two factor authentication (grid cards or tokens) into the framework.
However, there are many extension points in Spring Security - so I would appreciate it if you could give me some pointers as to whether I have gone about it in the right way.
The work flow would be: User logs in with their username and password > User is issued with a grid card challenge > Upon successful authentication they are granted access.
Here is the solution I have working at the moment.
Firstly I created a SimpleFormController (I called it GridChallengeController) that handles the two-factor authentication). If successful, users will be assigned the GridUser role and will be redirected to the page they originally requested.
Code:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
AcegiCrowdUserDetails details = (AcegiCrowdUserDetails) auth.getPrincipal();
details.addAuthority("GridUser");
UsernamePasswordAuthenticationToken existing = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(SecurityContextHolder.getContext().getAuthentication().getPrincipal(),
existing.getCredentials(), details.getAuthorities()));
SavedRequest saved = (SavedRequest) request.getSession().getAttribute(AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY);
return new ModelAndView(new RedirectView(saved.getFullRequestUrl()));
Then I extend the org.acegisecurity.ui.ExceptionTranslationFilter to allow for another authentication entry point. It will be triggered when the accessDecisionManager throws a TwoFactorAccessDeniedException (extends org.acegisecurity.AuthenticationException). Here are the relevant methods:
Code:
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
private AuthenticationEntryPoint twoFactorAuthenticationEntryPoint;
...
try {
chain.doFilter(request, response);
} catch (TwoFactorAccessDeniedException ex) {
handleException(request, response, chain, ex);
} catch (AuthenticationException ex) {
handleException(request, response, chain, ex);
} catch (AccessDeniedException ex) {
handleException(request, response, chain, ex);
} catch (ServletException ex) {
...
}
}
private void handleException(ServletRequest request, ServletResponse response, FilterChain chain, AcegiSecurityException exception) throws IOException, ServletException {
if (exception instanceof TwoFactorAccessDeniedException) {
sendStartTwoFactorAuthentication(request, response, chain, (AuthenticationException) exception);
} else if (exception instanceof AuthenticationException) {
sendStartAuthentication(request, response, chain, (AuthenticationException) exception);
} else if (exception instanceof AccessDeniedException) {
...
}
}
private void sendStartTwoFactorAuthentication(ServletRequest request, ServletResponse response, FilterChain chain, AuthenticationException reason) throws ServletException, IOException {
twoFactorAuthenticationEntryPoint.commence((HttpServletRequest) request, (HttpServletResponse) response, reason);
}
Finally, I extend org.acegisecurity.vote.AffirmativeBased to throw a TwoFactorAccessDeniedException when a user has not passed two-factor authentication (Users are allocated the GridUser role when they are authenticated with their grid card).
Code:
#Override
public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config) throws AccessDeniedException {
super.decide(authentication, object, config);
FilterInvocation f = (FilterInvocation) object;
if (!hasRole(authentication, "GridUser") && !hasRole(authentication, "ROLE_ANONYMOUS") && !f.getRequestUrl().equalsIgnoreCase("/gridchallenge.htm")) {
throw new TwoFactorAccessDeniedException("Need a grid logon");
}
}
Here are the relevant snippets of my security XML file:
Code:
<bean id="exceptionTranslationFilter" class="authentication.TwoFactorExceptionTranslationFilter">
<property name="authenticationEntryPoint">
<bean id="basicProcessingFilterEntryPoint" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl" value="/acegilogin.jsp"/>
<property name="forceHttps" value="false"/>
</bean>
</property>
<property name="twoFactorAuthenticationEntryPoint">
<bean id="gridProcessingFilterEntryPoint" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl" value="/gridchallenge.htm"/>
<property name="forceHttps" value="false"/>
</bean>
</property>
<property name="accessDeniedHandler">
<bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
<property name="errorPage" value="/accessDenied.jsp"/>
</bean>
</property>
</bean>
<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager">
<bean class="authentication.TwoFactorAffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="false"/>
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
<bean class="org.acegisecurity.vote.AuthenticatedVoter"/>
</list>
</property>
</bean>
</property>
<property name="objectDefinitionSource">
<value><![CDATA[
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/admin/**=Vaas_Administrator
/guard/**=Guard
/acegilogin.jsp=IS_AUTHENTICATED_ANONYMOUSLY
/gridchallenge.htm=IS_AUTHENTICATED_FULLY
/**=VaasUser
]]></value>
</property>
</bean>
Is there any thing should I add furthermore so that my app to work correctly?

Spring Security ignoring access-denied-handler with Method Level Security

I'm using Spring 3.2.4 and am unable to get Spring Security to redirect to my access-denied-handler when using Annotation based method level security. I have found several different posts about this, but to date, there does not seem to be any solutions that I have found.
My security.xml file:
<!-- need this here to be able to secure methods in components other than controllers (as scanned in applicationContext.xml) -->
<global-method-security secured-annotations="enabled" pre-post-annotations="enabled" jsr250-annotations="enabled" ></global-method-security>
<!-- Annotation/JavaConfig examples http://stackoverflow.com/questions/7361513/spring-security-login-page -->
<http use-expressions="true" entry-point-ref="authenticationEntryPoint">
<access-denied-handler ref="accessDeniedHandler"/>
<intercept-url pattern="/secure/login" access="permitAll" />
<intercept-url pattern="/secure/logout" access="permitAll" />
<intercept-url pattern="/secure/denied" access="permitAll" />
<session-management session-fixation-protection="migrateSession" session-authentication-error-url="/login.jsp?authFailed=true">
<concurrency-control max-sessions="10" error-if-maximum-exceeded="true" expired-url="/login.html" session-registry-alias="sessionRegistry"/>
</session-management>
<intercept-url pattern="/**" access="isAuthenticated()" />
<form-login default-target-url="/" authentication-failure-url="/secure/denied" />
<logout logout-url="/secure/logout" logout-success-url="/" />
<expression-handler ref="defaultWebSecurityExpressionHandler" />
</http>
<beans:bean id="authenticationEntryPoint" class="com.ia.security.LoginUrlAuthenticationEntryPoint">
<beans:constructor-arg name="loginFormUrl" value="/secure/login"/>
</beans:bean>
<beans:bean id="accessDeniedHandler" class="com.ia.security.AccessDeniedHandlerImpl">
<beans:property name="errorPage" value="/secure/denied"/>
</beans:bean>
My AccessDeniedHandlerImpl.java :
public class AccessDeniedHandlerImpl extends org.springframework.security.web.access.AccessDeniedHandlerImpl {
// SLF4J logger
private static final Logger logger = LoggerFactory.getLogger(AccessDeniedHandlerImpl.class);
#Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
logger.log("AccessDeniedException triggered!");
super.handle(request, response, accessDeniedException);
}
}
My Annotated Method:
#PreAuthorize("hasAuthority('ROLE_ZZZZ')")
public ModelAndView getUserInfo( #PathVariable long userId ){
ModelAndView mv = new ModelAndView();
User u = userService.findUser( userId );
mv.addObject("user", u);
return mv;
}
Is there anything special I need to do such that my access-denied-handler is called?
After several hours of searching around and tracing Spring code, I finally discovered what was happening. I am listing this here in case it is of value to someone else.
The access-denied-handler is used by the ExceptionTranslationFilter in case of an AccessDeniedException. However, the org.springframework.web.servlet.DispatcherServlet was first trying the handle the exception. Specifically, I had a org.springframework.web.servlet.handler.SimpleMappingExceptionResolver defined with a defaultErrorView. Consequently, the SimpleMappingExceptionResolver was consuming the exception by redirecting to an appropriate view, and consequently, there was no exception left to bubble up to the ExceptionTranslationFilter.
The fix was rather simple. Configure the SimpleMappingExceptionResolver to ignore all AccessDeniedException.
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView" value="uncaughtException" />
<property name="excludedExceptions" value="org.springframework.security.access.AccessDeniedException" />
<property name="exceptionMappings">
<props>
<prop key=".DataAccessException">dataAccessFailure</prop>
<prop key=".NoSuchRequestHandlingMethodException">resourceNotFound</prop>
<prop key=".TypeMismatchException">resourceNotFound</prop>
<prop key=".MissingServletRequestParameterException">resourceNotFound</prop>
</props>
</property>
</bean>
Now, whenever an AccessDeniedException is thrown, the resolver ignores it and allows it to bubble up the stack to the ExceptionTranslationFilter which then calls upon the access-denied-handler to handle the exception.
I run into the same issue. In my case there was already a #ControllerAdvise definied which should handle exceptions - so I added the AccessDeniedException directly:
#Component
#ControllerAdvice
public class ControllerBase {
...
#ExceptionHandler(value = AccessDeniedException.class)
public ModelAndView accessDenied() {
return new ModelAndView("redirect:login.html");
}
}
Good luck with it!
Extending the Erics answer with JavaConfig for SimpleMappingExceptionResolver to ignore AccessDeniedException so that it can be thrown as response and doesn't get swallowed by the SimpleMappingExceptionResolver.
#Configuration
#EnableWebMvc
public class AppConfig extends WebMvcConfigurerAdapter {
#Bean
public SimpleMappingExceptionResolver exceptionResolver() {
SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
exceptionResolver.setExcludedExceptions(AccessDeniedException.class);
return exceptionResolver;
}
}
Added on to Jessi answer above (https://stackoverflow.com/a/25948861/13215486). Note that if you want to tell the difference between Access Denied, and Access Forbidden, then you need to do a little more work.
#Component
#ControllerAdvice
public class ControllerBase {
...
#ExceptionHandler(value = AccessDeniedException.class)
public ModelAndView accessDenied(HttpServletRequest request) {
ModelAndView mav = new ModelAndView("redirect:login.html");
mav.setStatus(request.getRemoteUser() != null ? HttpStatus.FORBIDDEN : HttpStatus.UNAUTHORIZED);
return mav;
}
}

Integrating Atmosphere with Spring MVC

I want to provide push notifications in a Spring MVC project, using JDK 1.6 targeting all browsers. I followed this post and finally decided to go with Atmosphere.
For Server-sent events, My server controller is (source):
#Controller
public class AtmosphereController {
#RequestMapping(value="/getTime", method=RequestMethod.GET)
#ResponseBody
public void websockets(final AtmosphereResource atmosphereResource) {
final HttpServletRequest request = atmosphereResource.getRequest();
final HttpServletResponse response = atmosphereResource.getResponse();
atmosphereResource.suspend();
final Broadcaster bc = atmosphereResource.getBroadcaster();
bc.scheduleFixedBroadcast(new Callable<String>() {
public String call() throws Exception {
return (new Date()).toString();
}
}, 10, TimeUnit.SECONDS);
}
}
While running, I got org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.atmosphere.cpr.AtmosphereResource]: Specified class is an interface.
In solution to this, I got this relevant post. I added this to my dispatcher-servlet.xml:
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
</list>
</property>
</bean>
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean id= "atmosphereResource"
class="org.atmosphere.cpr.AtmosphereResourceImpl" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
Doing this, also results in a new error:
[cvc-complex-type.2.1: Element 'mvc:annotation-driven' must have no character or element information item [children], because the type's content type is empty.]
I've also tried this. Please help me inject AtmosphereResource in Spring Controller. Do I also need to update web.xml or some other configuration file to make it work or what part I'm missing. Please help!
Please also comment on other alternatives to provide Server-Side Events functionality. Thanks in advance!
Your argument-resolvers must be a class like this:
public class AtmosphereArgumentResolver implements HandlerMethodArgumentResolver {
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest httpServletRequest= webRequest.getNativeRequest(HttpServletRequest.class);
return Meteor.build(httpServletRequest).getAtmosphereResource();
}
#Override
public boolean supportsParameter(MethodParameter parameter) {
return AtmosphereResource.class.isAssignableFrom(parameter.getParameterType());
}
}
and try this:
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="com.yourpackage.AtmosphereArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
try Atmosphere 2.1.0-RC1 and following this document . All you need to do is to add the atmosphere-spring.jar to your dependency.
-- Jeanfrancois

Resources