Spring Boot Rest : Error 404 not found when posting JSON via Postman - rest

I am trying to invoke a POST service via Postman. My application is running on embedded tomcat server. However when I try to invoke the service, the error I get is "No mapping found for HTTP request with URI [/findrouting] in DispatcherServlet with name 'dispatcherServlet'"
It is not even recognizing http:localhost:8080/
RoutingRequest and RoutingResponse are the POJOs with getters and setters.
Am I missing something here. I did check lots of examples but didn't find any solution to my problem.
Please see my code below :
package com.ab.hello.ambassador.server;
import java.util.Arrays;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.context.ApplicationContext;
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(AmbassadorApplication.class, args);
System.out.println("List of Beans instantiated");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
}
}
package com.ab.hello.ambassador.server.controller;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.dp.connect.ambassador.server.RoutingRequest;
import com.dp.connect.ambassador.server.RoutingResponse;
#RestController
public class ApplicationController {
#RequestMapping("/")
public String index() {
return "Spring Boot POC Welcomes You!";
}
#RequestMapping(method = POST, value = "/findrouting", consumes = { APPLICATION_JSON_VALUE }, produces = {
APPLICATION_JSON_VALUE })
public RoutingResponse findRoute(#RequestBody RoutingRequest request) throws Exception {
// some business logic that would return response; as of now I have set it to null
RoutingResponse response = null;
return response;
}
}

Thanks for your time. After struggling for quite a long time. I figured out this piece of code. Added annotation #EnableWebMvc. This solved my problem.
#SpringBootApplication
#EnableWebMvc
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}

Try to define the path in your controller:
#RestController
#RequestMapping("/")
public class ApplicationController {
#RequestMapping
public String index() {
return "Spring Boot POC Welcomes You!";
}
#RequestMapping(method = POST, value = "/findrouting", consumes = { APPLICATION_JSON_VALUE }, produces = {
APPLICATION_JSON_VALUE })
public RoutingResponse findRoute(#RequestBody RoutingRequest request) throws Exception {
// some business logic that would return response; as of now I have set it to null
RoutingResponse response = null;
return response;
}
}

Change the below line
ApplicationContext ctx = SpringApplication.run(AmbassadorApplication.class, args);
with
ApplicationContext ctx = SpringApplication.run(Application.class, args);

according to spring docs , seems need to Override a config method:
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}

Related

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!

Why I am getting initialization error while running apache camel Test Case?

I am using below code for my Apache camel based application . I am using JUNIT4 and camel version 2.18 . And I am running test case in my Eclipse IDE.
I am using MockEndPoint concept for testing my API
package com.service;
import org.apache.camel.*;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
public class FilterTest extends CamelTestSupport {
#EndpointInject(uri = "mock:result")
protected MockEndpoint resultEndpoint;
#Produce(uri = "direct:start")
protected ProducerTemplate template;
#Override
public boolean isDumpRouteCoverage() {
return true;
}
#Test
public void testSendMatchingMessage() throws Exception {
String expectedBody = "<matched/>";
resultEndpoint.expectedBodiesReceived(expectedBody);
template.sendBodyAndHeader(expectedBody, "foo", "bar");
resultEndpoint.assertIsSatisfied();
}
#Test
public void testSendNotMatchingMessage() throws Exception {
resultEndpoint.expectedMessageCount(0);
template.sendBodyAndHeader("<notMatched/>", "foo", "notMatchedHeaderValue");
resultEndpoint.assertIsSatisfied();
}
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
public void configure() {
from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
}
};
}}
And I am getting below error for this code
FilterTest.testSendMatchingMessage
initializationError(org.junit.runner.manipulation.Filter)
java.lang.Exception: No tests found matching [{ExactMatcher:fDisplayName=testSendMatchingMessage], {ExactMatcher:fDisplayName=testSendMatchingMessage(com.service.FilterTest)], {LeadingIdentifierMatcher:fClassName=com.service.FilterTest,fLeadingIdentifier=testSendMatchingMessage]] from org.junit.internal.requests.ClassRequest#255316f2
at org.junit.internal.requests.FilterRequest.getRunner(FilterRequest.java:40)

How to set a global custom Jackson deserializer in Camel w/o spring using REST

I would like to set a global custom date/Time deserializer on a camel route that is configured with rest.
What I already found is Camel + Jackson : Register a module for all deserialization
But I do not have the unmarshal() method in the route, but use the
RestDefinition rest(String path)
method from
org.apache.camel.builder.RouteBuilder.
We do not use Spring, but plain Camel with Scala and REST all configuration done programmatically (no xml).
My current solution is to use
#JsonDeserialize(using = classOf[MyDeserializer])
annotation on every date/time field, but that is not a satisfying solution.
Does anybody have a clue how to configure Camel to use the custom deserializer everywhere?
By default camel use DATES_AS_TIMESTAMPS feature. to disable this featuer just add .dataFormatProperty("json.out.disableFeatures", "WRITE_DATES_AS_TIMESTAMPS")
Also you can add custom serializer:
module.addSerializer(Date.class, sample.new DateSerializer());
and binding with name json-jackson
jndiContext.bind("json-jackson", jackson);
example:
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.jackson.JacksonDataFormat;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.model.rest.RestBindingMode;
import org.apache.camel.spi.DataFormat;
import org.apache.camel.util.jndi.JndiContext;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
public class Samlpe3 {
public static void main(String[] args) throws Exception {
Samlpe3 sample = new Samlpe3();
//custom mapper
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule("DefaultModule", new Version(0, 0, 1, null, null, null));
module.addSerializer(Date.class, sample.new DateSerializer());
module.addSerializer(Person.class, sample.new PersonSerializer());
objectMapper.registerModule(module);
JacksonDataFormat jackson = new JacksonDataFormat(objectMapper, null);
JndiContext jndiContext = new JndiContext();
jndiContext.bind("json-jackson", jackson);
CamelContext context = new DefaultCamelContext(jndiContext);
context.addRoutes(new RouteBuilder() {
public void configure() throws Exception {
restConfiguration().component("jetty").bindingMode(RestBindingMode.json)
.host("0.0.0.0").contextPath("/test").port(8080)
//disableFeatures WRITE_DATES_AS_TIMESTAMPS
.dataFormatProperty("json.out.disableFeatures", "WRITE_DATES_AS_TIMESTAMPS")
;
rest("/v1/").produces("application/json")
.get("persons")
.to("direct:getPersons");
from("direct:getPersons")
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
exchange.getIn().setBody(new Person("Sergey", new GregorianCalendar().getTime()));
}
})
;
}
});
context.start();
Thread.sleep(60000);
context.stop();
}
public class DateSerializer extends JsonSerializer<Date> {
#Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers)
throws IOException, JsonProcessingException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String date = sdf.format(value);
gen.writeString(date);
}
}
public class PersonSerializer extends JsonSerializer<Person> {
#Override
public void serialize(Person value, JsonGenerator gen, SerializerProvider serializers)
throws IOException, JsonProcessingException {
gen.writeStartObject();
gen.writeFieldName("Changed_n");
gen.writeObject(value.getName() + " Changed");
gen.writeFieldName("Changed_b");
gen.writeObject(value.getBirthday());
gen.writeEndObject();
}
}
}
Person.java:
import java.util.Date;
public class Person {
private String name;
private Date birthday;
Person(String name, Date birhday){
System.out.println("Person");
this.setBirthday(birhday);
this.setName(name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}

Getting error "No serializer found for class org.springframework.data.mongodb.core.convert.DefaultDbRefResolver$LazyLoadingInterceptor"

I am using spring data mongodb,after doing lazy loading true i am getting error "No serializer found for class org.springframework.data.mongodb.core.convert.DefaultDbRefResolver$LazyLoadingInterceptor".
My domain class is
public class Preference extends BaseEntity {
#DBRef(lazy = true)
User user;
MetadataEnum preferenceType;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public MetadataEnum getPreferenceType() {
return preferenceType;
}
public void setPreferenceType(MetadataEnum preferenceType) {
this.preferenceType = preferenceType;
}
public List<Subtype> getSubtypes() {
return subtypes;
}
public void setSubtypes(List<Subtype> subtypes) {
this.subtypes = subtypes;
}
List<Subtype> subtypes = new ArrayList<Subtype>();
boolean enableSearch;
}
i have wasted my time alot,but i am unable to get suitable answer of it? anyone can help me to reslove it?
Thanks in advance
Add this configuration code for your requirement
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.convert.LazyLoadingProxy;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
#Configuration
public class LazyLoadingSerializationConfig {
#Bean
public ObjectMapper objectMapper() {
ObjectMapper om = new ObjectMapper();
final SimpleModule module = new SimpleModule("<your entity>", new Version(1, 0, 0,null));
module.addSerializer(LazyLoadingProxy.class, new LazyLoadingSerializer());
om.registerModule(module);
return om;
}
}
and
import java.io.IOException;
import org.springframework.data.mongodb.core.convert.LazyLoadingProxy;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class LazyLoadingSerializer extends JsonSerializer<LazyLoadingProxy> {
#Override
public void serialize(LazyLoadingProxy value, JsonGenerator jgen,
SerializerProvider provider) throws IOException,
JsonProcessingException {
jgen.writeStartObject();
jgen.writeStringField("id", value.toDBRef().getId().toString());
jgen.writeEndObject();
}
}
Hope this may help you!
switch off serializer for that bean because lazy loading with #DBRef works for list of entity only , jackson automatically serialize the #DBRef(lazy = true) List user but not for '#DBRef(lazy = true) User user. So you have to serialize it mannually or switch off serializer for that bean'.

External #EJB injection return null with RESTeasy and JBoss

I'm developing a RESTful Web Service on JBoss 6 Final with RESTeasy.
I have an external EJB and I want inject EJB on Web Service.
This is the code of EJB
BeanLocalInterface.java
#Local
public interface BeanLocalInterface {
public String sayHello();
}
Bean.java
#Stateless
#LocalBinding(jndiBinding = "BeanLocal")
public class Bean implements BeanLocalInterface {
#Override
public String sayHello() {
return "Hello!";
}
}
In another project I have a WS:
#Path("/ws")
public class HelloWorldResource {
#EJB(mappedName="BeanLocal")
private BeanLocalInterface bean;
#GET
#Path("/hello")
#Produces(MediaType.TEXT_PLAIN)
public String sayHello() {
return bean.sayHello();
}
}
MyRESTApplication.java
public class MyRESTApplication extends Application {
private Set<Object> singletons = new HashSet<Object>();
private Set<Class<?>> empty = new HashSet<Class<?>>();
public MyRESTApplication(){
singletons.add(new HelloWorldResource());
}
#Override
public Set<Class<?>> getClasses() {
return empty;
}
#Override
public Set<Object> getSingletons() {
return singletons;
}
}
bean is alwasy null!
I have tried this solution:
I have put #Stateless on HelloWorldResources
I have create beans.xml in WEB-INF
I have change MyRESTApplication with
#ApplicationPath("wsapp")
public class MyRESTApplication extends Application {
}
Can you help me?
PS: I don't have EAR e I don't want use it
I have managed to solve this. Firstly, remove all settings from web.xml regarding RESTeasy. You only need one adnotation --> #ApplicationPath
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("api")
public class MyRESTApplication extends Application {
//nothing required here, leave this empty
}
This is your session bean interface.
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
#Path("MyEJB")
public interface BeanInterface {
#Path("{username}")
#GET
#Produces(MediaType.TEXT_PLAIN)
public String sayHello(#PathParam("username") String username);
}
This is your session bean.
import javax.ejb.Stateless;
#Stateless
public class EJB implements BeanInterface{
#Override
public String sayHello(String username) {
return "Hello, "+username;
}
}
With JBOSS 7.1
Usage: http:// localhost:8080 /YOURAPPNAME/api/MyEJB/Fabio
Output:Hello, Fabio
Hope this helps.