Spring Data Mongodb WriteConcern working? - spring

I have the following Spring Data MongoDb Repository Java configuration:
#EnableMongoRepositories(basePackages= {"com.example.repositories.mongodb"})
public class MongoConfig extends AbstractMongoConfiguration {
private #Value("${mongo.host}") String mongoHost;
private #Value("${mongo.port}") int mongoPort;
private #Value("${mongo.database}") String mongoDatabase;
#Override
protected String getDatabaseName() {
return mongoDatabase;
}
#Override
public Mongo mongo() throws Exception {
MongoClientOptions options = MongoClientOptions.builder()
.connectionsPerHost(100)
.connectTimeout(120000)
.socketTimeout(120000)
.maxWaitTime(1200000)
.threadsAllowedToBlockForConnectionMultiplier(1500)
.writeConcern(WriteConcern.ACKNOWLEDGED)
.build();
MongoClient client = new MongoClient(new ServerAddress(mongoHost, mongoPort), options);
return client;
}
public #Bean PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
I am trying to figure out if the writeConcern is really turned on due to some jUnits not working when running repeatedly. If I place a breakpoint after the client is created above and inspect the client object I can see its property WriteConcern equals:
{w=null, wTimeout=null ms, fsync=null, journal=null}
Which suggests to me that it was not set to ACKNOWLEDGED.
Am I setting it properly and is there a way to see if the correct concern is set? None of the logger options I tried made it output.
My dependencies are:
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>${spring.data.mongodb}</version>
</dependency>

Related

Spring Integraton RSocket and Spring RSocket interaction issues

I created a new sample and slipted the codes into client and server side.
The complete codes can be found here.
There are 3 version of server side.
server None Spring Boot app, using Spring Integration RSocket InboundGateway.
server-boot Reuse Spring RSocket autconfiguration, and created ServerRSocketConnecter through ServerRSocketMessageHanlder.
server-boot-messsagemapping Not use Spring Integration, just use Spring Boot RSocket autconfiguration, and #Controller and #MessageMapping.
There are 2 versions of client.
client, Sending messages using Spring Integration Rocket OutboundGateway.
client-requester Send messages using RSocketRequester, not use Spring Integration at all.
The client and server interaction mode is REQUEST_CHANNEL, and connect server via TCP/localhost:7000.
server
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-rsocket</artifactId>
</dependency>
The application class:
#Configuration
#ComponentScan
#IntegrationComponentScan
#EnableIntegration
public class DemoApplication {
public static void main(String[] args) throws IOException {
try (ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(DemoApplication.class)) {
System.out.println("Press any key to exit.");
System.in.read();
} finally {
System.out.println("Exited.");
}
}
#Bean
public ServerRSocketConnector serverRSocketConnector() {
return new ServerRSocketConnector("localhost", 7000);
}
#Bean
public IntegrationFlow rsocketUpperCaseFlow(ServerRSocketConnector serverRSocketConnector) {
return IntegrationFlows
.from(RSockets.inboundGateway("/uppercase")
.interactionModels(RSocketInteractionModel.requestChannel)
.rsocketConnector(serverRSocketConnector)
)
.<Flux<String>, Flux<String>>transform((flux) -> flux.map(String::toUpperCase))
.get();
}
}
server-boot
Dependencies in pom.xml.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-rsocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-rsocket</artifactId>
</dependency>
application.properties
spring.rsocket.server.port=7000
spring.rsocket.server.transport=tcp
Application class.
#SpringBootApplication
#EnableIntegration
public class DemoApplication {
public static void main(String[] args) throws IOException {
SpringApplication.run(DemoApplication.class, args);
}
// see PR: https://github.com/spring-projects/spring-boot/pull/18834
#Bean
ServerRSocketMessageHandler serverRSocketMessageHandler(RSocketStrategies rSocketStrategies) {
var handler = new ServerRSocketMessageHandler(true);
handler.setRSocketStrategies(rSocketStrategies);
return handler;
}
#Bean
public ServerRSocketConnector serverRSocketConnector(ServerRSocketMessageHandler serverRSocketMessageHandler) {
return new ServerRSocketConnector(serverRSocketMessageHandler);
}
#Bean
public IntegrationFlow rsocketUpperCaseFlow(ServerRSocketConnector serverRSocketConnector) {
return IntegrationFlows
.from(RSockets.inboundGateway("/uppercase")
.interactionModels(RSocketInteractionModel.requestChannel)
.rsocketConnector(serverRSocketConnector)
)
.<Flux<String>, Flux<String>>transform((flux) -> flux.map(String::toUpperCase))
.get();
}
}
server-boot-messagemapping
Dependencies in pom.xml.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-rsocket</artifactId>
</dependency>
The application.properties.
spring.rsocket.server.port=7000
spring.rsocket.server.transport=tcp
The applcition class.
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
#Controller
class UpperCaseHandler {
#MessageMapping("/uppercase")
public Flux<String> uppercase(Flux<String> input) {
return input.map(String::toUpperCase);
}
}
client
In the client, the dependencies in the pom.xml is like.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-rsocket</artifactId>
</dependency>
The application class:
#SpringBootApplication
#EnableIntegration
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
public ClientRSocketConnector clientRSocketConnector() {
ClientRSocketConnector clientRSocketConnector = new ClientRSocketConnector("localhost", 7000);
clientRSocketConnector.setAutoStartup(false);
return clientRSocketConnector;
}
#Bean
public IntegrationFlow rsocketUpperCaseRequestFlow(ClientRSocketConnector clientRSocketConnector) {
return IntegrationFlows
.from(Function.class)
.handle(RSockets.outboundGateway("/uppercase")
.interactionModel((message) -> RSocketInteractionModel.requestChannel)
.expectedResponseType("T(java.lang.String)")
.clientRSocketConnector(clientRSocketConnector))
.get();
}
}
#RestController
class HelloController {
#Autowired()
#Lazy
#Qualifier("rsocketUpperCaseRequestFlow.gateway")
private Function<Flux<String>, Flux<String>> rsocketUpperCaseFlowFunction;
#GetMapping(value = "hello", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> uppercase() {
return rsocketUpperCaseFlowFunction.apply(Flux.just("a", "b", "c", "d"));
}
}
When running the client and server application, and try to access the http://localhost:8080/hello by curl.
When using server and server-boot which uses InboundGateway to handle messages, the output looks like this.
curl http://localhost:8080/hello
data:ABCD
When using server-boot-messagemapping, the output is woking as I expected:
data:A
data:B
data:C
data:D
client-requester
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-rsocket</artifactId>
</dependency>
The application class:
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
#RestController
class HelloController {
Mono<RSocketRequester> requesterMono;
public HelloController(RSocketRequester.Builder builder) {
this.requesterMono = builder.connectTcp("localhost", 7000);
}
#GetMapping(value = "hello", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> uppercase() {
return requesterMono.flatMapMany(
rSocketRequester -> rSocketRequester.route("/uppercase")
.data(Flux.just("a", "b", "c", "d"))
.retrieveFlux(String.class)
);
}
}
When running this client and the 3 servers, and try to access the http://localhost:8080/hello by curl.
When using server and server-boot which uses InboundGateway to handle messages, it throws a class cast exception.
When using server-boot-messagemapping, the output is woking as I expected:
data:A
data:B
data:C
data:D
I do not know where is the problem of the configuration of InboundGateway and OutboundGateway?
Thank you for such a detailed sample!
So, what I see. Both clients (plain RSocketRequester and Spring Integration) work well with plain RSocket server.
To make them working with Spring Integration server you have to do this changes:
The server side:
Add .requestElementType(ResolvableType.forClass(String.class)) into an RSockets.inboundGateway() definition, so it will know to what to convert an incoming payloads.
The client side:
.data(Flux.just("a\n", "b\n", "c\n", "d\n")).
Currently the server side of Spring Integration doesn't treat an incoming Flux as a stream of independent payloads. So, we try to connect all of them into a single value.
The new line delimiter is an indicator that we expect independent values. Spring Messaging on its side does exactly opposite: it checks for multi-value expected type and decode every element in the incoming Flux in its map() instead of an attempt for the whole Publisher decoding.
It's going to be kinda breaking change, but possibly need to consider to fix RSocketInboundGateway logic to be consistent with regular #MessageMapping for RSocket support. Feel free to raise a GH issue!

How to bypass Redis session on Spring Boot application on Redis connection failure?

I have implemented Redis session management using LettuceConnectionFactory on my Spring Boot java application by following some of the sample applications out there and the Spring documentation. It works fine when it can connect to the Redis server. But the application doesn't respond when the connection fails because it repeatedly trying to connect. I have done a lot of research to find a solution but couldn't find anything yet. The questions I have are,
Is there a way to switch to Spring Session (it could even be a JDBC session) when it can't connect to Redis after certain number of retries?
Is there a way to set the number of retries in the connection factory? I don't see any property for this in spring.redis properties list.
Any help is appreciated.
Updated on 11/4/2019
Application properties:
spring.session.store-type=redis
spring.session.redis.flush-mode=on-save
spring.session.redis.namespace=spring:session
spring.redis.host=*****.windows.net
spring.redis.port=6380
spring.redis.ssl=true
spring.redis.password=****************
spring.redis.timeout=5000ms
spring.redis.custom.command.timeout=1000ms
HttpSessionConfig.java:
#Configuration
#PropertySource("classpath:application.properties")
#EnableRedisHttpSession
public class HttpSessionConfig extends AbstractHttpSessionApplicationInitializer {
Logger logger = LoggerFactory.getLogger(HttpSessionConfig.class);
#Value("${spring.redis.host}")
private String redisHostName;
#Value("${spring.redis.port}")
private int redisPort;
#Value("${spring.redis.password}")
private String redisPassword;
private #Value("${spring.redis.custom.command.timeout}")
Duration redisCommandTimeout;
private #Value("${spring.redis.timeout}")
Duration socketTimeout;
#Bean
LettuceConnectionFactory lettuceConnectionFactory() {
final SocketOptions socketOptions = SocketOptions.builder().connectTimeout(socketTimeout).build();
final ClientOptions clientOptions = ClientOptions.builder()
.socketOptions(socketOptions)
.build();
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.commandTimeout(redisCommandTimeout)
.clientOptions(clientOptions)
.readFrom(ReadFrom.SLAVE_PREFERRED)
.build();
RedisStandaloneConfiguration serverConfig = new RedisStandaloneConfiguration(redisHostName,
redisPort);
final LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(serverConfig,
clientConfig);
return lettuceConnectionFactory;
}
#Bean
public RedisTemplate<Object, Object> sessionRedisTemplate() {
final RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(lettuceConnectionFactory());
return template;
}
#Bean
public ConfigureRedisAction configureRedisAction() {
return ConfigureRedisAction.NO_OP;
}
}
Dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>biz.paluch.redis</groupId>
<artifactId>lettuce</artifactId>
<version>3.5.0.Final</version>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>5.1.6.RELEASE</version>
<optional>true</optional>
</dependency>
Please let me know if more details are required.
As us we're using
#Profile(value = "production") for RedisHttpSession and
#Profile(value = "development") for DevSessionConfig
then use can setting spring.profiles.active in application.properties

Query parameters reading spring integration

I am new to Spring Integration, I just started looking into the specification. My requirement is to get the HTTP request
(example : http://localhost:8080/LoginCheck?name=xyz&dob=zyz).
Can anybody guide me, how to proceed as i googled and found some information that we can use inbound gateway to read the parameters, my requirement like get the Http client data and do some process and finally respond to client in XML format.
I got stucked in reading the input data only.
You have to get the payload of the received message. There should be a Map with the request parameters.
I have made a simple SI DSL application that does just that
#SpringBootApplication
public class JmsResponderApplication {
public static void main(String[] args) {
SpringApplication.run(JmsResponderApplication.class, args);
}
#Bean
public HttpRequestHandlingMessagingGateway httpGate() {
HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(true);
RequestMapping requestMapping = new RequestMapping();
requestMapping.setMethods(HttpMethod.GET);
requestMapping.setPathPatterns("/foo");
gateway.setRequestMapping(requestMapping);
gateway.setRequestChannel(requestChannel());
return gateway;
}
#Bean
public DirectChannel requestChannel() {
return MessageChannels.direct().get();
}
#Bean
public IntegrationFlow flow() {
return IntegrationFlows.from(requestChannel())
.handle(new MessageHandler() {
#Override
public void handleMessage(Message<?> m) throws MessagingException {
Object payload = m.getPayload();
System.out.println(payload); // the payload is a Map that holds the params
System.out.println(m);
}
})
.get();
}
}
It's a simple Spring boot starter project with this dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-java-dsl</artifactId>
<version>1.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Source - here

spring data neo4j Embedded : Connect to localhost:7474 failed: Connection refused

Having this configuration :
#Configuration
#EnableNeo4jRepositories("com.mydb.repository")
#EnableTransactionManagement
public class Neo4jConfig extends Neo4jConfiguration {
#Bean
#Override
public Neo4jServer neo4jServer() {
return new RemoteServer( "http://localhost:7474" );
}
#Bean
public GraphDatabaseService graphDatabaseService() {
return new GraphDatabaseFactory().newEmbeddedDatabaseBuilder( new File("./mydb.db") )
.setConfig( ServerSettings.auth_enabled, Boolean.FALSE.toString() )
.newGraphDatabase();
}
#Bean
#Override
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public Session getSession() throws Exception {
return super.getSession();
}
#Bean
#Override
public SessionFactory getSessionFactory() {
return new SessionFactory("com.mydb.model");
}
}
I have 2 errors :
1_ I can't keep the Session in scope "session"
2_ I have the exception : Connect to localhost:7474 failed: Connection refused:
The server is Embedded, it should start without instaling any server on my local machine ?
Spring-boot 1.3.0 (Last) and
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-neo4j</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
Version 4.0 of Spring Data for Neo4j doesn't support embedded version of Neo4j.
You need to install Neo4j on your local machine.
"SDN 4.1 will support embedded version." #Luanne

Making Aspectj work on a Spring servlet bean

I am trying to get an aspectprofiler working on a Jersey servlet registered in a spring project. The aspectprofiler is loaded, but don't notice when methods within the Jersey servlet are run.
#EnableAutoConfiguration
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class App {
public static void main(final String[] args) {
final SpringApplicationBuilder sab = new SpringApplicationBuilder(ConsolidatedCustomerMasterApp.class);
sab.run(args);
}
#Bean
public ServletRegistrationBean jerseyServlet() {
final ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), "/*");
registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyInitialization.class.getName());
return registration;
}
#Bean
public AspectProfiler profiler() {
return new AspectProfiler();
}
}
...
public class JerseyInitialization extends ResourceConfig {
public JerseyInitialization() {
packages("com.example.package");
}
...
package com.example.package;
//imports
#Path("/test")
public class RestService {
#GET
#Path("test")
#Produces(MediaType.TEXT_PLAIN)
public String test() {
return "Something";
}
}
...
#Aspect
public class AspectProfiler {
private static final DefaultApplicationProfiler PROFILER = new DefaultApplicationProfiler(
Arrays.<ProfilerOperator> asList(
new StatsdProfilerOperator(),
new LoggingProfilerOperator())
);
private static final String REST_MATCHER =
"execution(* com.example.package..*.*(..))";
#Around(REST_MATCHER)
public Object around(final ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("test");
return PROFILER.around(joinPoint);
}
}
On top of making the Jersey resource classes Spring #Components (and #ComponentScaning for them), you also need to make the ResourceConfig a Spring #Component also. You can see in the Spring Boot JerseyAutoConfigurer that it autowires the ResourceConfig, which it uses for the ServletContainer registration.
One thing to also note is that it creates its own ServletRegistrationBean
public ServletRegistrationBean jerseyServletRegistration() {
ServletRegistrationBean registration = new ServletRegistrationBean(
new ServletContainer(this.config), this.path);
addInitParameters(registration);
registration.setName("jerseyServlet");
return registration;
}
When you declare your own, you are overriding this one. You are not adding any special functionality that is not already provided, so just leave the default. Any Jersey specific configurations can be added in the application.properties file or through code configurations.
As for dependencies, I'll just assume you have all the right dependencies. The following are what I used to test
<!-- all 1.2.7.RELEASE -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</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-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
See Also:
spring-boot-sample-jersey - from project samples
ยง26.2 JAX-RS and Jersey - from Spring Boot docs

Resources