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

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

Related

Spring MVC with Thymeleaf and Tomcat 8 UTF-8 Encoding Issue

I have some problem about encoding. According to this referance I have to do all of this and I did. But my encoding problem still remain after those. I am using Spring MVC, Spring Security, Thymeleaf, Tomcat 8 and Maven 3.
Here are some screeshots.
I passed string with model and it is working.
This is validation message came from my message.properties file. Encoding error occurred.
I am trying to save some record to db and error occurred again.
Here is what I have done.
I edited Tomcat's server.xml config and added UTF-8 encoding.
server.xml
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
useBodyEncodingForURI="true"
URIEncoding="UTF-8"
redirectPort="8443" />
I created CharacterEncodingFilter in my config folder.
CharacterEncodingFilter.java
#WebFilter(urlPatterns = {"/*"})
public class CharacterEncodingFilter implements Filter {
#Override
public void init(FilterConfig filterConfig)
throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
servletRequest.setCharacterEncoding("UTF-8");
servletResponse.setContentType("text/html; charset=UTF-8");
filterChain.doFilter(servletRequest, servletResponse);
}
#Override
public void destroy() {
}
}
All my html files has this meta tag line for encoding.
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
My Thymeleaf configs are also set to UTF-8 encoding.
#Bean
#Description("Thymeleaf template resolver serving HTML 5")
public ServletContextTemplateResolver templateResolver() {
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver();
templateResolver.setPrefix("/WEB-INF/html/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("LEGACYHTML5");
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setCacheable(false);
return templateResolver;
}
#Bean
#Description("Thymeleaf template engine with Spring integration")
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
return templateEngine;
}
#Bean
#Description("Thymeleaf view resolver")
public ThymeleafViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setContentType("text/html;charset=UTF-8");
viewResolver.setCharacterEncoding("utf-8");
return viewResolver;
}
I created my database with UTF-8 encoding like this.
postgres=# CREATE USER unicodeuser WITH PASSWORD 'mypass123';
postgres=# CREATE DATABASE unicode WITH ENCODING 'UTF8' OWNER unicodeuser;
postgres=# GRANT ALL ON DATABASE unicode TO unicodeuser;
I created JAVA_OPTS environment variable for encoding.
JAVA_OPTS=-DuriEncoding=UTF-8 -Dfile.encoding=UTF-8
I also changed my pom.xml files for UTF-8 encoding.
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
I tried everything on the internet but still can't find a solution. Any idea about what I am missing would be great.
fatiherdem, in all your cases you dont pay attention to Spring Security. Try that https://stackoverflow.com/a/23051264/4380830 answer. I have the same stack of technology with you and this answer work for me
I had the very same issue with a validation message and all the code solutions were still not enough. In my case using Intellij as editor I checked Transaparent native-to-ascii conversion to solve the issue. Please see my complete answer here.
You can use this trick of thymeleaf comment before process:

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

How do I get the user when logs incorrectly with Spring Security?

I am using Spring security for interceptor to user wrong logged, but I can not find the way to do.
I specifically want to save the user logged in last, but I can't figure out how to achieve this.
Please help me.
Yes you can do that. You can define following tag in tag in your configuration xml file.
<security:form-login login-page="/sessionexpired"
login-processing-url="/j_spring_security_check"
default-target-url="/submitLogin"
always-use-default-target="true"
authentication-failure-handler-ref="customAuthenticationFailureHandler"/>
You can see the last parameter set authentication-failure-handler-ref its value is a refenrece to following bean defined in the same xml file.
<bean id="customAuthenticationFailureHandler" class="com.xxx.xxx.xxx.CustomFilter">
<constructor-arg type="String" value="loginfailed"></constructor-arg>
<constructor-arg type="org.hibernate.SessionFactory" ref="sessionFactory"></constructor-arg>
</bean>
The class defined in this bean is your own class that will get the information about the failed login details.
public class CustomFilter extends SimpleUrlAuthenticationFailureHandler {
private String defaultFailureUrl;
private SessionFactory sessionFactory;
public CustomFilter(String defaultFailureUrl,SessionFactory sessionFactory) {
super();
this.defaultFailureUrl = defaultFailureUrl;
this.sessionFactory = sessionFactory;
}
#Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response, AuthenticationException exception)
throws IOException, ServletException {
// TODO Auto-generated method stub
String userName = request.getParameter("j_username");;
/*System.out.println("CustomFilter Begins");
System.out.println("CustomeFilter.username :: " + userName);
System.out.println("getMessage :: " + exception.getMessage());
System.out.println("exception :: " + exception.getClass().getSimpleName());
System.out.println("RemoteAddr :: " + request.getRemoteAddr()); */
}
}
When the Authentication will be failed then method onAuthenticationFailure of this class will be called and you can get the user details there to log in database or log file.
Hope this helps you. Cheers.

Map url with language identifier

Is there a nice way to resolve locale based on the URL and in the other hand map requests without any additional requirement ?
For example
http://example.com/ru/news
http://example.com/iw/news
and in the controller still use the standard mappings
#Controller
#RequestMapping(value = "/news")
public class NewsController {
// Controller methods ...
}
You could write a custom interceptor that works like LocaleChangeInterceptor
Here's a sample implementation that uses a regex pattern (most of the code is copied from LocaleChangeInterceptor):
public class CustomLocaleChangeInterceptor extends HandlerInterceptorAdapter {
private Pattern localePattern;
public void setLocalePattern(final String localePattern) {
Assert.isTrue(localePattern.matches(".*\\(.*\\).*"), "Your pattern needs to define a match group");
this.localePattern = Pattern.compile(localePattern);
}
#Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler)
throws ServletException {
final String pathTranslated = request.getPathTranslated();
if (pathTranslated != null) {
final Matcher matcher = localePattern.matcher(pathTranslated);
if (matcher.find()) {
final String newLocale = matcher.group(1);
if (newLocale != null) {
final LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
if (localeResolver == null) {
throw new IllegalStateException("No LocaleResolver found: not in a DispatcherServlet request?");
}
final LocaleEditor localeEditor = new LocaleEditor();
localeEditor.setAsText(newLocale);
localeResolver.setLocale(request, response, (Locale) localeEditor.getValue());
}
}
}
// Proceed in any case.
return true;
}
}
Wire it like this:
<bean id="localeChangeInterceptor"
class="foo.bar.CustomLocaleChangeInterceptor">
<property name="localePattern" value="\b([a-z]{2})\b"/>
</bean
I'm not aware of an out-of-the-box solution for this, but it's easy enough to implement using a custom interceptor and some wisely chosen mappings.
Write an implementation of HandlerInterceptor which implements preHandle so that the locale string is extracted from the request URI, and then tag the request with that locale (see the source code for the similar LocalChangeInterceptor, which does a similar thing to what you need, but uses a request parameter instead of a path variable).
Then wire it up using <mvc:interceptor> e.g.
<mvc:interceptors>
<mvc:interceptor>
<mapping path="/*"/>
<bean class="x.y.MyInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
You can then loosen up the request mapping on your controller to tolerate (and ignore) the locale part of the URI:
#Controller
#RequestMapping(value = "*/news")
public class NewsController {
// Controller methods ...
}
Take a look at http://lrkwz.github.com/URLManagedLocale/ you can simply drop the dependency in your pom file and configure the interceptor and localeresolver.

Resources