JAX RPC service returning a map like structure - jax-rpc

I do know that the hashmap data structure is not allowed in JAX-RPC webservice.
But i would like to return data which looks like this in my service.
Atrribute,<Key><value>,Atrribute,<Key><value>,Atrribute,<Key><value> ..
Any idea how would i do this please

TO Return Map like structue you need to wrap it in Wrapper class.
Wrap your Map into JAXBMap as shown below and return it.
package myexample;
import java.util.Map;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class JAXBMap<T, K> {
Map<T, K> map;
public Map<T, K> getMap() {
return map;
}
public void setMap(Map<T, K> map) {
this.map = map;
}
public JAXBMap(Map<T, K> map) {
super();
this.map = map;
}
public JAXBMap() {
super();
}
}

Related

Collect result from flatMap using Spring WebFlux

I have a interface Event which internally calls the checkEvent() of each implementation and gives EventResponse as object. So the final output using all implementation gives me List
As these are not synchronized, try to execute in async way to get the output fast.
I am trying to achieve the same thing using Spring Web Flux.
However not able to acheive the final result i.e.List
My event interface is like:
public interface IEventProvider {
EventResponse check(EventRequest request);
}
I have 3 implementation of this which are as below:
MumbaiEventProvider
import org.springframework.stereotype.Component;
#Component
public class MumbaiEventProvider implements IEventProvider {
#Override
public EventResponse check(EventRequest request) {
return EventResponse.builder().location("Mumbai").build();
}
}
DelhiEventProvider
import org.springframework.stereotype.Component;
#Component
public class DelhiEventProvider implements IEventProvider {
#Override
public EventResponse check(EventRequest request) {
return EventResponse.builder().location("Delhi").build();
}
}
ChennaiEventProvider
import org.springframework.stereotype.Component;
#Component
public class ChennaiEventProvider implements IEventProvider {
#Override
public EventResponse check(EventRequest request) {
return EventResponse.builder().location("Chennai").build();
}
}
I have tried using below code but not helping as final output is not as expected:
#Service
public class EventProviderHandler {
private final List<IEventProvider> providers;
#Autowired
public EligibilityHandler(List<IEventProvider> providers) {
this.providers = providers;
}
public Mono<ServerResponse> getEligibilities(ServerRequest request) {
List<EventResponse> eventList = new ArrayList<>();
Mono<EventRequest> eventRequestMono = request.bodyToMono(EventRequest.class);
List<EventResponse> events = Flux.fromIterable(providers).parallel().flatMap(eventProvider -> {
eventList.add(eventProvider.check(eventRequestMono));
return eventList;
}).collectList();
}
}
What is expected is to return:
List<EligibilityResponse> eligibilities = Flux.fromIterable(providers).parallel().flatMap(eligibilityProvider -> {
eligibilities.add(eligibilityProvider.check(eligibilityRequestMono));
return eligibilities;
})

Multi-Tenancy in Reactive Spring boot application using mongodb-reactive

How can we create a multi-tenant application in spring webflux using Mongodb-reactive repository?
I cannot find any complete resources on the web for reactive applications. all the resources available are for non-reactive applications.
UPDATE:
In a non-reactive application, we used to store contextual data in ThreadLocal but this cannot be done with reactive applications as there is thread switching. There is a way to store contextual info in reactor Context inside a WebFilter, But I don't how get hold of that data in ReactiveMongoDatabaseFactory class.
Thanks.
I was able to Implement Multi-Tenancy in Spring Reactive application using mangodb. Main classes responsible for realizing were: Custom MongoDbFactory class, WebFilter class (instead of Servlet Filter) for capturing tenant info and a ThreadLocal class for storing tenant info. Flow is very simple:
Capture Tenant related info from the request in WebFilter and set it in ThreadLocal. Here I am sending Tenant info using header: X-Tenant
Implement Custom MondoDbFactory class and override getMongoDatabase() method to return database based on current tenant available in ThreadLocal class.
Source code is:
CurrentTenantHolder.java
package com.jazasoft.demo;
public class CurrentTenantHolder {
private static final ThreadLocal<String> currentTenant = new InheritableThreadLocal<>();
public static String get() {
return currentTenant.get();
}
public static void set(String tenant) {
currentTenant.set(tenant);
}
public static String remove() {
synchronized (currentTenant) {
String tenant = currentTenant.get();
currentTenant.remove();
return tenant;
}
}
}
TenantContextWebFilter.java
package com.example.demo;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
#Component
public class TenantContextWebFilter implements WebFilter {
public static final String TENANT_HTTP_HEADER = "X-Tenant";
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
if (request.getHeaders().containsKey(TENANT_HTTP_HEADER)) {
String tenant = request.getHeaders().getFirst(TENANT_HTTP_HEADER);
CurrentTenantHolder.set(tenant);
}
return chain.filter(exchange).doOnSuccessOrError((Void v, Throwable throwable) -> CurrentTenantHolder.remove());
}
}
MultiTenantMongoDbFactory.java
package com.example.demo;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoDatabase;
import org.springframework.dao.DataAccessException;
import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory;
public class MultiTenantMongoDbFactory extends SimpleReactiveMongoDatabaseFactory {
private final String defaultDatabase;
public MultiTenantMongoDbFactory(MongoClient mongoClient, String databaseName) {
super(mongoClient, databaseName);
this.defaultDatabase = databaseName;
}
#Override
public MongoDatabase getMongoDatabase() throws DataAccessException {
final String tlName = CurrentTenantHolder.get();
final String dbToUse = (tlName != null ? tlName : this.defaultDatabase);
return super.getMongoDatabase(dbToUse);
}
}
MongoDbConfig.java
package com.example.demo;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.ReactiveMongoClientFactoryBean;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
#Configuration
public class MongoDbConfig {
#Bean
public ReactiveMongoTemplate reactiveMongoTemplate(MultiTenantMongoDbFactory multiTenantMongoDbFactory) {
return new ReactiveMongoTemplate(multiTenantMongoDbFactory);
}
#Bean
public MultiTenantMongoDbFactory multiTenantMangoDbFactory(MongoClient mongoClient) {
return new MultiTenantMongoDbFactory(mongoClient, "test1");
}
#Bean
public ReactiveMongoClientFactoryBean mongoClient() {
ReactiveMongoClientFactoryBean clientFactory = new ReactiveMongoClientFactoryBean();
clientFactory.setHost("localhost");
return clientFactory;
}
}
UPDATE:
In reactive-stream we cannot store contextual information in ThreadLocal any more as the request is not tied to a single thread, So, This is not the correct solution.
However, Contextual information can be stored reactor Context in WebFilter like this. chain.filter(exchange).subscriberContext(context -> context.put("tenant", tenant));. Problem is how do get hold of this contextual info in ReactiveMongoDatabaseFactory implementation class.
Here is my very rough working solution for Spring WebFlux - they have since updated the ReactiveMongoDatabaseFactory - getMongoDatabase to return a Mono
Create web filter
public class TenantContextFilter implements WebFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(TenantContextFilter.class);
#Override
public Mono<Void> filter(ServerWebExchange swe, WebFilterChain wfc) {
ServerHttpRequest request = swe.getRequest();
HttpHeaders headers = request.getHeaders();
if(headers.getFirst("X-TENANT-ID") == null){
LOGGER.info(String.format("Missing X-TENANT-ID header"));
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
}
String tenantId = headers.getFirst("X-TENANT-ID");
LOGGER.info(String.format("Processing request with tenant identifier [%s]", tenantId));
return wfc.filter(swe)
.contextWrite(TenantContextHolder.setTenantId(tenantId));
}
}
Create class to get context (credit to somewhere I found this)
public class TenantContextHolder {
public static final String TENANT_ID = TenantContextHolder.class.getName() + ".TENANT_ID";
public static Context setTenantId(String id) {
return Context.of(TENANT_ID, Mono.just(id));
}
public static Mono<String> getTenantId() {
return Mono.deferContextual(contextView -> {
if (contextView.hasKey(TENANT_ID)) {
return contextView.get(TENANT_ID);
}
return Mono.empty();
}
);
}
public static Function<Context, Context> clearContext() {
return (context) -> context.delete(TENANT_ID);
}
}
My spring security setup (all requests allowed for testing)
#EnableWebFluxSecurity
#EnableReactiveMethodSecurity
public class SecurityConfig {
#Bean
public SecurityWebFilterChain WebFilterChain(ServerHttpSecurity http) {
return http
.formLogin(it -> it.disable())
.cors(it -> it.disable()) //fix this
.httpBasic(it -> it.disable())
.csrf(it -> it.disable())
.securityContextRepository(NoOpServerSecurityContextRepository.getInstance())
.authorizeExchange(it -> it.anyExchange().permitAll()) //allow anonymous
.addFilterAt(new TenantContextFilter(), SecurityWebFiltersOrder.HTTP_BASIC)
.build();
}
}
Create Tenant Mongo DB Factory
I still have some clean-up work for defaults etc...
public class MultiTenantMongoDBFactory extends SimpleReactiveMongoDatabaseFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(MultiTenantMongoDBFactory.class);
private final String defaultDb;
public MultiTenantMongoDBFactory(MongoClient mongoClient, String databaseName) {
super(mongoClient, databaseName);
this.defaultDb = databaseName;
}
#Override
public Mono<MongoDatabase> getMongoDatabase() throws DataAccessException {
return TenantContextHolder.getTenantId()
.map(id -> {
LOGGER.info(String.format("Database trying to retrieved is [%s]", id));
return super.getMongoDatabase(id);
})
.flatMap(db -> {
return db;
})
.log();
}
}
Configuration Class
#Configuration
#EnableReactiveMongoAuditing
#EnableReactiveMongoRepositories(basePackages = {"com.order.repository"})
class MongoDbConfiguration {
#Bean
public ReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory() {
return new MultiTenantMongoDBFactory(MongoClients.create("mongodb://user:password#localhost:27017"), "tenant_catalog");
}
#Bean
public ReactiveMongoTemplate reactiveMongoTemplate() {
ReactiveMongoTemplate template = new ReactiveMongoTemplate(reactiveMongoDatabaseFactory());
template.setWriteResultChecking(WriteResultChecking.EXCEPTION);
return template;
}
}
Entity Class
#Document(collection = "order")
//getters
//setters
Testing
Create two mongo db's with same collection, put different documents in both
In Postman I just did a get request with the "X-TENANT-ID" header and database name as the value (e.g. tenant-12343 or tenant-34383) and good to go!

Drools : Getting the catched word in a list in THEN

Below is my pojo class
-----------------------------------pojo_Classes2.RootDoc.java-----------------------------------
package pojo_Classes2;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"content",
"person"
})
public class RootDoc {
#JsonProperty("content")
private String content;
#JsonProperty("person")
private List<String> person = null;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("content")
public String getContent() {
return content;
}
#JsonProperty("content")
public void setContent(String content) {
this.content = content;
}
#JsonProperty("person")
public List<String> getPerson() {
return person;
}
#JsonProperty("person")
public void setPerson(List<String> person) {
this.person = person;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
Here is the type of rule which i want to apply
$list1=[gaurav,gagan,anshu....]
...................................................................................................................
Rule1
If
content contains any of the above $list1
Then
Retrieve which name was captured in content and set person the person name in then
............................................................................................................
For eg. gaurav and gagan were captured in content then set get that gaurav and gagan were matched in content and get them back in then part.
Is it possible in drools
Yes, but create object of your class like:
when
$rd : Rootdoc(****your query****);
then
rd.setPerson(query);
end

Map that extends TreeMap deserialized as empty map on client

I need to pass a map with a custom field to client.
just TreeMap works fine, but not descendants of TreeMap. If I change TreeMap to HashMap, it also works fine.
GWT 2.5.1
public class MyMap extends TreeMap<String, String> {
public String s;
public MyMap() {
}
public MyMap(String s) {
this.s = s;
}
}
public class GreetingServiceImpl extends RemoteServiceServlet implements GreetingService {
#Override
public Map<String, String> testSelfMap() {
Map<String, String> c = new MyMap("abc");
c.put("k", "v");
return c;
}
}
#RemoteServiceRelativePath("greet")
public interface GreetingService extends RemoteService {
Map<String, String> testSelfMap();
}
public interface GreetingServiceAsync {
void testSelfMap(AsyncCallback<Map<String, String>> async);
}
public class HW implements EntryPoint {
GreetingServiceAsync serv = GWT.create(GreetingService.class);
public void onModuleLoad() {
serv.testSelfMap(new AsyncCallback<Map<String, String>>() {
#Override
public void onSuccess(Map<String, String> result) {
System.out.println(result.get("k"));
}
#Override
public void onFailure(Throwable caught) {
caught.printStackTrace();
}
});
}
}

GXT 3, Grid compilation problems

I am trying to get a GXT 3 Grid running, but getting the following exception when running GWT compile form Eclipse:
java.lang.NullPointerException
at com.sencha.gxt.data.rebind.ModelKeyProviderCreator.getObjectType(ModelKeyProviderCreator.java:40)
Code to launch the grid and make it visible:
DataProperties dp = GWT.create(DataProperties.class);
List<ColumnConfig<MyGridData, ?>> css = new LinkedList<ColumnConfig<MyGridData, ?>>();
css.add(new ColumnConfig<MyGridData, String>(dp.name(), 200, "Name"));
css.add(new ColumnConfig<MyGridData, String>(dp.value(), 200, "Value"));
ColumnModel<MyGridData> cm = new ColumnModel<MyGridData>(css);
ListStore<MyGridData> s = new ListStore<MyGridData>(dp.key());
s.add(new MyGridData("name1","value1"));
s.add(new MyGridData("name2","value2"));
s.add(new MyGridData("name3","value3"));
s.add(new MyGridData("name4","value4"));
Grid<MyGridData> g = new Grid<MyGridData>(s, cm);
addToDisplay(g);
the grid data bean:
public class MyGridData{
private String name;
private String value;
public MyGridData(String name, String value) {
super();
this.name = name;
this.value = value;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
}
The supporting DataProperties class:
import com.google.gwt.editor.client.Editor.Path;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.data.shared.ModelKeyProvider;
import com.sencha.gxt.data.shared.PropertyAccess;
public interface DataProperties extends PropertyAccess {
#Path("name")
ModelKeyProvider key();
ValueProvider<MyGridData, String> name();
ValueProvider<MyGridData, String> value();
}
From your code:
public interface DataProperties extends PropertyAccess {
#Path("name")
ModelKeyProvider key();
ValueProvider<MyGridData, String> name();
ValueProvider<MyGridData, String> value();
}
You've got an error here - without generics, the compiler can't figure out what you need. Your IDE should be warning you about your raw use of both ProperyAccess and ModelKeyProvider. Both of those need to refer to MyGridData. Try this instead:
public interface DataProperties extends PropertyAccess<MyGridData> {//here
#Path("name")
ModelKeyProvider<MyGridData> key();// and here
ValueProvider<MyGridData, String> name();
ValueProvider<MyGridData, String> value();
}