Spring boot 2 reactive web websocket conflict with datarest - spring-boot

I'm using spring boot 2 to create a project and use websocket using reactive web dependency. My application is worked correctly until I add datarest dependency. after I add datarest dependency application give
' failed: Error during WebSocket handshake: Unexpected response code: 404
is any way to resolve this conflict?.
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.integration/spring-integration-file -->
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-file</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
WebSocketConfiguration
#Configuration
public class WebSocketConfiguration {
#Bean
public IntegrationFlow fileFlow(PublishSubscribeChannel channel, #Value("file://${HOME}/Desktop/in") File file) {
FileInboundChannelAdapterSpec in = Files.inboundAdapter(file).autoCreateDirectory(true);
return IntegrationFlows.from(
in,
p -> p.poller(pollerFactory -> {
return pollerFactory.fixedRate(1000);
})
).channel(channel).get();
}
#Bean
#Primary
public PublishSubscribeChannel incomingFilesChannel() {
return new PublishSubscribeChannel();
}
#Bean
public WebSocketHandlerAdapter webSocketHandlerAdapter() {
return new WebSocketHandlerAdapter();
}
#Bean
public WebSocketHandler webSocketHandler(PublishSubscribeChannel channel) {
return session -> {
Map<String, MessageHandler> connections = new ConcurrentHashMap<>();
Flux<WebSocketMessage> publisher = Flux.create((Consumer<FluxSink<WebSocketMessage>>) fluxSink -> {
connections.put(session.getId(), new ForwardingMessageHandler(session, fluxSink));
channel.subscribe(connections.get(session.getId()));
}).doFinally(signalType -> {
channel.unsubscribe(connections.get(session.getId()));
connections.remove(session.getId());
});
return session.send(publisher);
};
}
#Bean
public HandlerMapping handlerMapping(WebSocketHandler webSocketHandler) {
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
handlerMapping.setOrder(10);
handlerMapping.setUrlMap(Collections.singletonMap("/ws/files", webSocketHandler));
return handlerMapping;
}
}

spring-boot-starter-data-rest brings spring-boot-starter-web as a transitive dependency (so basically Spring MVC). This makes Spring Boot configure your application as a Spring MVC web application.
Spring Data REST does not currently support Spring WebFlux (see this issue for more information on that).
Your only choice is to remove the Spring Data REST dependency, as you can't have both Spring MVC and Spring WebFlux in the same Spring Boot application.

Related

Use tomcat instead of netty for rscket

I want to leverage on servlets and filters so I want to use tomcat and, in general, servlet 3.1 to handle the communication.
I tried to do the following:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-rsocket</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-reactor-netty</artifactId>
</exclusion>
</exclusions>
It works bringing up a tomcat but I lost the endpoint!
Here's what I do to register it:
spring.rsocket.server:
transport: websocket
mapping-path: /topics
and:
#Configuration
public class RSocketConfig {
#Bean
public Mono<RSocketRequester> rSocketRequester(
RSocketStrategies rSocketStrategies,
RSocketProperties rSocketProps) {
return RSocketRequester.builder()
.rsocketStrategies(rSocketStrategies)
.connectWebSocket(getURI(rSocketProps));
}
private URI getURI(RSocketProperties rSocketProps) {
return URI.create(String.format("ws://localhost:%d%s",
rSocketProps.getServer().getPort(), rSocketProps.getServer().getMappingPath()));
}

Injecting Specific DataSource in Spring Boot 2.0.2 Release

When trying to autowire DataSource by using Spring Boot 2.0.2 RELEASE, it gives the following error and cannot manage to inject it:
'Could not autowire. There is more than one bean of 'DataSource' type.'
The beans are created in DataSourceConfiguration class for HikariDataSource, BasicDataSource and for Tomcat.
Is there a way of qualifying one of them and avoid creation of the remaining ones?
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
public SecurityConfig() {
this.usersByUsernameQuery = // definitions here
this.authoritiesByUsernameQuery = //
this.allowedAuthorities = //
}
#Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth, final DataSource dataSource) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery(usersByUsernameQuery)
.authoritiesByUsernameQuery(authoritiesByUsernameQuery);
}
}
pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
</dependencies>
application.properties:
spring.datasource.url=jdbc:mysql://localhost:9400/test
spring.datasource.username=root
spring.datasource.password=test
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

How to configure netty in spring boot 2

By default spring web flux uses netty which is single threaded event loop. How to configure spring boot so that a thread will be created for each core.
Thanks,
Lokesh
As described in the Spring Boot reference documentation, you can customize the Reactor Netty web server with a NettyServerCustomizer.
Here's an example with Spring Boot 2.1:
#Component
public class MyNettyWebServerCustomizer
implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
#Override
public void customize(NettyReactiveWebServerFactory factory) {
factory.addServerCustomizers(new EventLoopNettyCustomizer());
}
}
class EventLoopNettyCustomizer implements NettyServerCustomizer {
#Override
public HttpServer apply(HttpServer httpServer) {
LoopResources loopResources = LoopResources.create(...);
return httpServer.runOn(loopResources);
}
}
You can change your dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- Exclude the Tomcat dependency -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Use Jetty instead -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
https://docs.spring.io/spring-boot/docs/current/reference/html/howto-embedded-web-servers.html

Spring Boot/Spring Integration Servlet Context/DelegatingWebMvcConfiguration issue

I'm not sure the best way to word this issue, because I have no idea why this happens.
When I start up my Spring Boot application without the spring-integration-core jar, my app starts up fine. The second I add the spring-boot-starter-integration or any spring-integration-core library to my Maven pom file the servlet context is always null on my DelegatingWebMvcConfiguration class and I get the following error:
java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
After doing some debugging, I see the DelegatingWebMvcConfiguration class initialized before the EmbeddedWebApplicationContext sets the servlet context for my application, which makes sense why the servlet context is null for my DelegatingWebMvcConfiguration and set for all the other classes initialized with postProcessBeforeInitialization in ServletContextAwareProcessor. Simply removing the Spring Integration dependency fixes the issue, but I'd like to use Spring Integration in my Boot application.
My question is what would cause the DelegatingWebMvcConfiguration to be created before the EmbeddedWebApplicationContext can call prepareEmbeddedWebApplicationContext method.
Spring dependencies using Spring Boot 1.1.8.RELEASE:
org.springframework.boot
spring-boot-starter-web
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-solr</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.hateoas</groupId>
<artifactId>spring-hateoas</artifactId>
<version>0.16.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-core</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-cassandra</artifactId>
<version>1.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>4.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>4.1.2.RELEASE</version>
</dependency>
My root configuration file:
#Configuration
#EnableAutoConfiguration
#EnableAsync
#EnableEntityLinks
#EnableSpringDataWebSupport
#ComponentScan
#PropertySource(value = "file:../application.properties",
ignoreResourceNotFound = true)
public class ApplicationInitializer extends SpringBootServletInitializer
implements EmbeddedServletContainerCustomizer {
public static void main(String[] args) throws Exception {
SpringApplication.run(ApplicationInitializer.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application){
return application.sources(ApplicationInitializer.class);
}
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
container.setContextPath(APPLICATION_ROOT_PATH);
}
// Global beans defined here
}
Update: After trying many different things, I found out after disabling my SchedulingConfigurer my app now starts. Here is my SchedulingConfigurer
#Configuration
#EnableScheduling
#EnableConfigurationProperties(SchedulingProperties.class)
public class SchedulingConfig implements SchedulingConfigurer {
//Inject Runnables and properties
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskScheduler());
// call taskRegistrar.addFixedRateTask with runnable and property rate value
}
#Bean(destroyMethod = "shutdown")
public Executor taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(Runtime.getRuntime().availableProcessors());
return taskScheduler;
}
}

Spring Boot + Apache Camel HTTP component crashes at start: missing EmbeddedServletContainerFactory bean

Just taking Spring-Boot for a spin and decided to mix in Camel because I need some arcane Headers work in the rest client I am working on. Setting up the application was fine until I added the camel-http component to my POM, then I get this on init:
Exception in thread "main" org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
I've havent got the first idea of where to start to look for the problem. I gather Spring Boot will look up the classpath and try to wire stuff up, so is there a way for the to block the Camel packages from being acted on or something of the sort?
Complete log of the start up in this Gist
Here's my main aplication code:
#ComponentScan
#EnableAutoConfiguration
public class Application {
private static ApplicationContext ctx;
public static void main(String[] args) throws Exception {
ctx = SpringApplication.run(Application.class, args);
//Right outta Spring 4 docs
System.out.println("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
//---
// FIXME: ugly hack to allow some POC woek while wait for proper Camel/Spring4 unit tests fix.
Application app = new Application();
app.executeTests();
}
/**
* Dev QOL - unit tests are broken for now, see:
* https://issues.apache.org/jira/browse/CAMEL-7074
* <p/>
* Waiting for fix (Too lay to checkout and build my own Camel)
*/
private void executeTests() throws Exception {
testAuth();
}
#Bean
DefaultCamelContext camelCtx() throws Exception {
DefaultCamelContext camel = new DefaultCamelContext();
camel.addRoutes(cryptsyRouteBuilder());
camel.start();
return camel;
}
#Bean
public CryptsyRouteBuilder cryptsyRouteBuilder() throws Exception{
CryptsyRouteBuilder bean = new CryptsyRouteBuilder();
bean.setCryptsy(cryptsy());
return bean;
}
#Bean
public Cryptsy cryptsy() throws IOException {
return new Cryptsy();
}
protected void testAuth() throws Exception {
ProducerTemplate producer = camelCtx().createProducerTemplate();
producer.requestBody("direct:start", "Why, hullo there", String.class);
}
}
And my POM dependencies:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>0.5.0.BUILD-SNAPSHOT</version>
</parent>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--<dependency>-->
<!--<groupId>org.springframework.boot</groupId>-->
<!--<artifactId>spring-boot-starter-actuator</artifactId>-->
<!--</dependency>-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- Camel -->
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring-javaconfig</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-quartz</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test-spring</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-http</artifactId>
<version>${camel.version}</version>
</dependency>
<!-- Assorted -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
</dependencies>
<properties>
<start-class>xxx.xxxx.Application</start-class>
<camel.version>2.12.2</camel.version>
</properties>
The exception is telling you that Spring Boot thinks you want to build a web server, but can't find the right dependencies on the classpath. The most obvious reason for that in your case would be that the HTTP dependencies you added included Servlet APIs. I see no reason why you need that for a client app, but only you would know whether you need it or not. Maybe you can exclude it?
If you do need the Servlet dependencies and just want to explicitly tell Boot that you aren't creating a web application you can set the property spring.main.web_environment=false, or use the SpringApplication (or SpringApplicationBuilder) API directly to set the same flag. See docs here for background information.

Resources