How to replace one document with WebFlux and MongoDB Reactive frameworks? - java

I have an old project that I would like to modernize using WebFlux and MongoDB Reactive frameworks but I'm absolutely stuck at the update method. I have no clue what to pass in the subscribe method. The replaceOne call returns a Publisher but how I should handle it? One of the tips I read was about using the SubscriberHelpers class from mongo-java-driver-reactivestreams but it's missing from the recent versions (4.4). Any help would be appreciated.
mongoDatabase.getCollection("players", Player.class).replaceOne(
Filters.eq("email", player.getEmail()),
player,
new ReplaceOptions().upsert(true)
).subscribe( ??? );
Update: I created a DummySubscriber class to manage the subscription which seems working properly.
mongoDatabase.getCollection("players", Player.class).replaceOne(
Filters.eq("email", player.getEmail()),
player,
new ReplaceOptions().upsert(true)
).subscribe(new DummySubscriber<>());
private static class DummySubscriber<T> implements Subscriber<T> {
#Override
public void onSubscribe(Subscription subscription) {
subscription.request(Integer.MAX_VALUE);
}
#Override
public void onNext(T t) {
}
#Override
public void onError(Throwable throwable) {
System.out.println("Error while updating data");
}
#Override
public void onComplete() {
System.out.println("Data has been updated");
}
}

After looking at your example i could see that the problem is that you are not using the ReactiveMongoTemplate when doing your calls against the database.
You decided to use the raw MongoDatabase object, which is more low level which does not provide you with the webflux api.
MongoDatabase only implement the standard reactivestreams specification, which does only support something implementing Subscriber<T> as subscribers.
I recommend not using the low level MongoDatabase object and instead use the higher level abstractions.
If you are going to use spring webflux you should be using either the ReactiveRepositories that spring provides, or if you want to be low level use the ReactiveMongoTemplate
private final ReactiveMongoTemplate reactiveMongoTemplate;
#Autowired
public PlayerRepository(ReactiveMongoTemplate reactiveMongoTemplate) {
this.reactiveMongoTemplate = reactiveMongoTemplate;
}
public void savePlayers(Player player) {
final Query query = new Query();
query.addCriteria(Criteria.where("email").is(player.getEmail()));
final Update update = new Update();
update.set("name", player.getName());
update.set("email", player.getEmail());
reactiveMongoTemplate.findAndModify(query, update, Player.class)
.subscribe(
updatedPlayer -> System.out.println("updated player"),
error -> System.out.println("Something went wrong: " + error.getCause()),
() -> System.out.println("update is finished"));
}
I strongly suggest you learn how to use regular MongoTemplate without webflux before you go on to use webflux and the ReactiveMongoTemplate
There are several tutorials, about using MongoTemplate and the ReactiveMongoTemplate
https://www.baeldung.com/spring-data-mongodb-reactive
https://hantsy.github.io/spring-reactive-sample/data/data-mongo.html

Related

Using Hibernate Search 6 - How to prevent automatic indexing for a specific entity?

We would like to handle the indexing ourselves for one of our Hibernate Search entities.
Is it possible to disable auto indexing within Hibernate Search 6 for a specific entity? Similar to the older Hibernate Search global setting: hibernate.search.indexing_strategy = manual
I've searched through the documentation but haven't seen this mentioned.
hibernate.search.indexing_strategy = manual was for all entity types, not for a specific one.
The feature you're looking for has already been filed as HSEARCH-168, and is currently planned for Hibernate Search 6.2.
In the meantime, I think the best you can do would be to rely on a hack. It won't be as efficient as what we envision for HSEARCH-168, but it's a start:
Implement a RoutingBridge which, based on a switch, will either disable indexing completely (both adding and removing the entity from the index) or behave normally as if there was no routing bridge:
public class ManualIndexingRoutingBinder implements RoutingBinder {
private static volatile boolean indexingEnabled = false;
public static synchronized void withIndexingEnabled(Runnable runnable) {
indexingEnabled = true;
try {
runnable.run();
}
finally {
indexingEnabled = false;
}
}
#Override
public void bind(RoutingBindingContext context) {
context.dependencies()
.useRootOnly();
context.bridge(
Book.class,
new Bridge()
);
}
public static class Bridge implements RoutingBridge<Book> {
#Override
public void route(DocumentRoutes routes, Object entityIdentifier, Book indexedEntity,
RoutingBridgeRouteContext context) {
if ( indexingEnabled ) {
routes.addRoute();
}
else {
routes.notIndexed();
}
}
#Override
public void previousRoutes(DocumentRoutes routes, Object entityIdentifier, Book indexedEntity,
RoutingBridgeRouteContext context) {
if ( indexingEnabled ) {
// So that Hibernate Search will correctly delete entities if necessary.
// Only useful if you use SearchIndexingPlan for manual indexing,
// as the MassIndexer never deletes documents.
routes.addRoute();
}
else {
routes.notIndexed();
}
}
}
}
Apply that routing bridge to the entity types you want to control:
#Entity
#Indexed(routingBinder = #RoutingBinderRef(type = ManualIndexingRoutingBinder.class))
public class Book {
// ...
}
The routing bridge will effectively disable automatic indexing.
To index manually, do this:
ManualIndexingRoutingBinder.withIndexingEnabled(() -> {
Search.mapping(entityManagerFactory).scope(Book.class)
.massIndexer()
.startAndWait();
});
I didn't test this exactly, so please report back, but in principle this should work.

Asynchronous http request processing (non blocking way) - DeferredResult and how to remove it

I kind of hit the wall with DeferredResult. We have really old pattern where we have Interfaces that contains all rest annotations and implementation of them. Also other clients (microservices) uses those interfaces to map communicate with each others (they are importing them as a module and make proxy rest calls). But there is a problem somebody hacked a bit this approach and we had two different declarations one for clients without DeferredResult and one with it on implementation side. When we tried to reflect changes for clients there is a problem a lot of them needs to change a way of communication. So i've been thinking of removing DeferredResult from method signature and just use result.
My question is how to do it in non blocking way in Spring?
Let's say i have this kind of code
#Component
public class ExampleSO implements ExampleSOController {
private final MyServiceSO myServiceSO;
public ExampleSO(MyServiceSO myServiceSO) {
this.myServiceSO = myServiceSO;
}
#Override
public DeferredResult<SOResponse> justForTest() {
CompletableFuture<SOResponse> responseCompletableFuture = myServiceSO.doSomething();
DeferredResult<SOResponse> result = new DeferredResult<>(1000L);
responseCompletableFuture.whenCompleteAsync(
(res, throwable) -> result.setResult(res)
);
return result;
}
}
where:
#RestController
public interface ExampleSOController {
#PostMapping()
DeferredResult<SOResponse> justForTest();
}
and:
#Component
public class MyServiceSO {
public CompletableFuture<SOResponse> doSomething() {
CompletableFuture<SOResponse> completableFuture = new CompletableFuture<>();
Executors.newCachedThreadPool().submit(() -> {
Thread.sleep(500);
completableFuture.complete(new SOResponse());
return null;
});
return completableFuture;
}
}
How could i achieve something like this:
#RestController
public interface ExampleSOController {
#PostMapping()
SOResponse justForTest();
}
Without removing async benefits ?

How to create a custom error/exception handler for graphql?

So, I have been coding a microservice which uses GraphQL's java implementation for APIs. GraphQL enforces some level of validation for the supplied queries by client. However, in cases when the issue occurs inside resolving the query, I have seen graphql show messages which exposes the internals of the micr-service.
What do I need? A way to handle all exceptions/errors thrown from resolver functions in such a way that I can sanitize the exceptions/errors before GraphQL creates corresponding response.
I have looked up official documentation and many stack over flow questions, but failed to find any place it talks about handling. If I found any, they were for previous versions and are no more supported.
Some of the links I referred -
1. https://www.howtographql.com/graphql-java/7-error-handling/
2. GraphQL java send custom error in json format
3. https://www.graphql-java.com/documentation/v13/execution/
I have already done the below things like -
Creating custom handler
#Bean
public GraphQLErrorHandler errorHandler() {
return new CustomGraphQLErrorHandler();
}
public class CustomGraphQLErrorHandler implements GraphQLErrorHandler {
#Override
public List<GraphQLError> processErrors(List<GraphQLError> errors) {
List<GraphQLError> clientErrors = errors.stream()
.filter(this::isClientError)
.collect(Collectors.toList());
List<GraphQLError> serverErrors = errors.stream()
.filter(this::isSystemError)
.map(GraphQLErrorAdapter::new)
.collect(Collectors.toList());
List<GraphQLError> e = new ArrayList<>();
e.addAll(clientErrors);
e.addAll(serverErrors);
return e;
}
private boolean isSystemError(GraphQLError error) {
return !isClientError(error);
}
private boolean isClientError(GraphQLError error) {
return !(error instanceof ExceptionWhileDataFetching || error instanceof Throwable);
}
}```
Expected behavior - The control would reach to `processErrors` method. Actual - It doesn't reach there.
You need to override the errorsPresent method in GraphQLErrorHandler to return true when an error is passed into that method. Something like:
#Override
public boolean errorsPresent(List<GraphQLError> errors) {
return !CollectionUtils.isEmpty(errors);
}

Migration from Play 2.4.1 to 2.5.6 - Sockets

I have updated my Play Framework version from 2.4.1 to 2.5.6 but now I have a problem with the web sockets management.
I have a Controller class where method liveUpdate() return a WebSocket<String> instance.
In this method I use WebSocket.whenReady() using Out<String> in a HashMap<Out<String>, String> where the key is the client output stream and the value is a String that contains the language information because when I need to send a broadcast message I iterate the HashMap.
Now all this is removed or deprecated in 2.5.6!
Searching the web I found that the new implementation is based on Akka Streams using the Flow class but I have no idea how to adapt my code.
WebSocket.whenReady() is replaced by WebSocket.Text.accept()
Out<?> is replaced by akka stream with Flow class
This is my code:
Alarms.java
public class Alarms extends Controller {
#Inject
private ActiveAlarms activeAlarms;
[...]
public WebSocket liveUpdate() {
return WebSocket.whenReady((in, out) -> {
in.onMessage(language ->{
activeAlarms.register(out, language);
});
in.onClose(() -> activeAlarms.unregister(out));
});
}
[...]
}
ActiveAlarms.java
public class ActiveAlarms{
private HashMap<Out<String>, String> websockets = new HashMap<>();
[...]
public void register(Out<String> out, String language) {
websockets.put(out, language);
updateWebsockets(out, language);
}
public void unregister(Out<String> out) {
websockets.remove(out);
}
private void updateWebsockets(Out<String> s, String lang){
if(s == null) return;
List<AlarmEvent> alarmsList = AlarmEvent.findActive();
ArrayList<AlarmEvent> translatedAlarmsList = new ArrayList<>();
//translate
alarmsList.forEach(e ->{
if(e != null) {
e.setAlarm(e.getAlarm().translate(checkLanguage(lang)));
translatedAlarmsList.add(e);
}
});
//WRITE TO SOCKET
String alarms = Json.stringify(Json.toJson(translatedAlarmsList));
try {
s.write(alarms);
} catch (Exception e2) {
Logger.debug("EX ActiveAlarms --> updateWebSocket " + e2.getMessage());
}
}
private void updateWebsockets(){
websockets.forEach(this::updateWebsockets);
}
[...]
}
Any idea on how to convert my code to the new implementation of WebSocket ?
I think you can easily migrate from one to the other. Did you check this documentation?
If you want to manage the Socket with Akka streams, this is the corresponding documentation part.
Let me know if you still need help to migrate, I'll help you.

Java Design: How to expose and encapsulate a vendor API?

I know basic Java, but I struggle sometimes with object orientation design.
There is a vendor api I use, and I wanted to wrap it to be reusable as a lib in other projects.
All the services from the vendor are different classes and have no hierarchy and so on, but I have no option to change it.
So I want to use composition and ensure I don't repeat myself.
I thought initially to create a service that would receive the parameters that are common to all services, and this service would implement the api.
I tried refactoring this code here and there, and I'm pretty sure this design I'm trying has some great problems as I noticed when trying to create unit tests :)
How could I achieve a better design?
Code as of now:
/* This is how I call the service from the vendor today */
class VendorConsumer {
void exampleCake() {
VendorServiceCake vendorServiceCake = new VendorServiceCake();
VendorApiCake cakeApi = a.getCakeApi(1234);
cakeApi.authenticate("user", "password");
cakeApi.cookDeliciousCake(CakeIngredients ingredients);
}
void exampleSellPie() {
VendorServiceSellPie vendorServiceSellPie = new VendorServiceSellPie();
VendorApiSellPie apiSellPie = a.getPieApi(1234); //same parameters as above
apiSellPie.authenticate("user", "password"); //same parameters as above
apiSellPie.sellDeliciousPie(List<Customer> customer);
}
}
// ---------------------------------------------
/* Below is what I'm trying to do */
class UsageTest {
// This is how users of my .jar would call it
void usage() {
BakeryService service = new BakeryServiceCake("user", "password", 1234);
List<Cake> cakeList = service.cookDeliciousCake(CakeIngredients ingrediets);
}
void usage2() {
BakeryService service = new BakeryServiceSellPie("user", "password", 1234);
List<Payments> payments = service.sellDeliciousPie(List<Customer> customer);
}
}
class BakeryService { //is this class useless?
public BakeryService(String user, String pass, int parameterNeeded) {
}
private void checkParameters() {
//do some checkings of the parameters
}
}
class BakeryServiceCake extends BakeryService implements KitchenCakeApi {
KitchenCakeApi api;
public BakeryServiceCake(String user, String pass, int parameterNeeded) {
super(user, pass, parameterNeeded);
this.api = new KitchenCakeApiImpl(user, pass, parameterNeeded)
}
#Override
public void authenticate() {
api.authenticate();
}
#Override
public void cookDeliciousCake(CakeIngredients ingredients) {
api.cookDeliciousCake(ingredients);
}
}
interface KitchenCakeApi {
void authenticate();
void cookDeliciousCake(CakeIngredients ingredients);
}
class KitchenCakeApiImpl implements KitchenCakeApi {
private VendorServiceCake vendorServiceCake;
private VendorApiCake cakeApi;
public KitchenCakeApiImpl(String user, String pass, int parameterNeeded) {
vendorServiceCake = new VendorServiceCake();
cakeApi = a.getCakeApi(parameterNeeded); // that 1234
}
#Override
public void authenticate() {
cakeApi.authenticate("user", "password");
}
#Override
public void cookDeliciousCake(CakeIngredients ingredients) {
cakeApi.cookDeliciousCake(CakeIngredients ingredients);
}
}
Thanks!
The API's generally have some hierarchy/structure to it but you will need a keen eye to observe it, let's just say Java File handling is based on decorator pattern that too almost exact text book implementation.
Hard to answer without seeing the vendor API.
Probable Approach -
Use Adapter pattern help to encapsulates you from third party API.
Adapter has a cost to it which is lots of code to write but maintenance is easier.
If Adapters explode use it with proxy pattern to make a life bit easier.
Facade can help you in case of complex operations but this could be just an add-on in your case.
Two cents - http://www.oodesign.com/ go through design patterns just as an overview and see the problem it solves if it rings a bell go deeper else move on.
Happy designing.

Categories