Null Pointer Exception runiing AWS Lambda With Spring boot - java

I'm creating a sample of web service using Spring boot. When I run it in the localhost and test with Postman, it works fine. But when I deploy the application on AWS Lambda, it throws a nullpointerException.
When I was testing locally and on AWS Lambda this was working fine, until I started using #autowired
This is the project structure
The endpont or handle request is in the class LoginController
#Controller
#RequestMapping(value="api/auth/v1")
public class LoginController {
#Autowired
IClientCredentialService _clientCredentialService;
#RequestMapping(value = "/login", method = RequestMethod.POST, headers = "Accept=application/json")
public ResponseEntity<?> login(#RequestBody ClientCredential clientCredential){
//For testing from Postman and AWS, clientCredential is not null.
if(clientCredential == null) {
return new ResponseEntity<VOGenericResponse>(new VOGenericResponse(HttpStatus.BAD_REQUEST.value(), HttpStatus.BAD_REQUEST.name()), HttpStatus.BAD_REQUEST);
}
if(clientCredential.getClientId() == null || clientCredential.getClientId().equals("")) {
return new ResponseEntity<VOGenericResponse>(new VOGenericResponse(HttpStatus.BAD_REQUEST.value(), HttpStatus.BAD_REQUEST.name()), HttpStatus.BAD_REQUEST);
}
if(clientCredential.getClientSecret() == null || clientCredential.getClientSecret().equals("")) {
return new ResponseEntity<VOGenericResponse>(new VOGenericResponse(HttpStatus.BAD_REQUEST.value(), HttpStatus.BAD_REQUEST.name()), HttpStatus.BAD_REQUEST);
}
else {
ClientCredential client = _clientCredentialService.findClientCredential(clientCredential.getClientId(), clientCredential.getClientSecret());
//TODO Obtener token aquí
Token token = new Token();
token.setCode(HttpStatus.OK.value());
token.setDescription(HttpStatus.OK.name());
//Temp
token.setAccessToken("123");
token.setCreatedDate("Hoy");
token.setUpdatedDate("Hoy");
token.setExpiresIn("Mañana");
token.setStartIn("Hoy");
token.setTokenId("321");
token.setTokenType("bearer");
token.setUpdatedDate("hoy");
return new ResponseEntity<Token>(token, HttpStatus.OK);
}
The interface IClientCredentialService, escencially has a method, and the class ClientCredentialServiceImpl, implements that interface:
#Service("clientCredentialService")
#Transactional
public class ClientCredentialServiceImpl implements IClientCredentialService{
#Override
public ClientCredential findClientCredential(String clientId, String clientSecret) {
return new ClientCredential();
}
}
This is the main class:
#SpringBootApplication
public class AwsLambdaSurveyAuthServerLoginV1Application {
public static void main(String[] args) {
SpringApplication.run(AwsLambdaSurveyAuthServerLoginV1Application.class, args);
}
}
And the classes ClientCredential and Token, has a constructor and some getters and setters.
When I run it from postman, It works fine, I get a 200 http response:
{
"code": 200,
"description": "OK",
"tokenId": "321",
"accessToken": "123",
"tokenType": "bearer",
"expiresIn": "Mañana",
"startIn": "Hoy",
"createdDate": "Hoy",
"updatedDate": "hoy"
}
but, when I run It from AWS Lambda y get this error:
{
"errorMessage": "java.lang.NullPointerException",
"errorType": "java.lang.NullPointerException",
"stackTrace": [
"com.gns.survey.authserver.login.controller.LoginController.login(LoginController.java:47)",
"sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)",
"sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)",
"sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)",
"java.lang.reflect.Method.invoke(Method.java:498)"
]
}
Anyone know what the error is? I really appreciate your help.
/Edit: Method with null validation

Related

Getting values from external API using Spring Boot - Rest Template

I have an external API (like this - "https://www.example.com/en/products.json") and once hit the url it gives a json response like this:
{
"product1": {
"title": "Digital Adopter",
"description": "product description",
"image": ""
},
"product2": {
"title": "Router",
"description": "product2 description",
"image": ""
}
}
I want to use RestTemplate to consume above url(https://www.example.com/en/products.json) and tried following code. But didn't get any response or any error. I have less experience in development using Spring Boot RestTemplate and Can someone help me to fix this?
JsontestApplication .java
#SpringBootApplication
public class JsontestApplication {
public static void main(String[] args) {
SpringApplication.run(JsontestApplication.class, args);
}
#Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
Controller Class - Controller1.java
#RestController
public class Controller1 {
#Autowired
RestTemplate restTemplate;
#RequestMapping(value = "/jkl", method = RequestMethod.GET)
public String getProductList() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity <String> entity = new HttpEntity<String>(headers);
return restTemplate.exchange("https://www.example.com/en/products.json", HttpMethod.GET, entity, String.class).getBody();
}
}
In the application.properties file, I have set the port as server.port=8765 and application is running without any error. This is the url I hit using the Postman - http://localhost:8765/jkl

Fallback Factory not working to handle Custom Exception in Feign Client

My requirement is to access the custom exception thrown from first service along with it's body content in the second service
I have tried 2 things so far, FallbackFactory and ErrorDecoder, out of which only Fallback factory worked for me. Error decoder did not have the message of the exception which was thrown from other service. Here is the sample code that I found in another question:
There will be 2 services: inventory-service and product-service
inventory-service
InventoryController.java
#RestController
#RequestMapping("/inventories")
public class InventoryController {
private final ProductServiceClient productService;
public InventoryController(ProductServiceClient productService) {
super();
this.productService = productService;
}
#GetMapping
public ResponseEntity<?> companyInfo() {
return productService.hello();
}
}
ProductServiceClient.java
#FeignClient(name = "product-service", url = "http://localhost:9100", fallbackFactory = ProductServiceClientFallback.class)
public interface ProductServiceClient {
#GetMapping("/products")
ResponseEntity<?> hello();
}
#Component
class ProductServiceClientFallback implements FallbackFactory<ProductServiceClient> {
#Override
public ProductServiceClient create(Throwable cause) {
return new ProductServiceClient() {
#Override
public ResponseEntity<?> hello() {
System.out.println("hello!! fallback reason was " + cause.getMessage());
return ResponseEntity.ok().build();
}
};
}
}
product-service
ProductController.java
#RestController
#RequestMapping(value = "/products")
public class ProductController {
#GetMapping
public String hello() throws Exception {
if (true) {
throw new Exception("Service B Exception...");
}
return "Hello World";
}
}
ProductControllerAdvice.java
#RestControllerAdvice
public class ProductControllerAdvice {
#ExceptionHandler
public ResponseEntity<?> handleException(Exception exception) {
return new ResponseEntity<>("Caused due to : " + exception.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
So, when /inventories api is triggered in Inventory controller, it triggers a call to product-service via Feign Client and on product-service side, I throw a custom exception with a message, I have to access that message in my inventory-service.
To get that I have implemented fallback factory and it worked in a test-workspace since I got an output like this in console of inventory-service
hello!! fallback reason was status 500 reading ProductServiceClient#hello(); content:
Caused due to : Service B Exception...
But, my problem is when I try the similar approach with the applications that I'm working on, I did not get the message of exception, instead I got an out put like this:
reached fallback on workflow side, reason: status 400 reading ProvisioningServiceProxy#executeOrderAction(Long,Long,String)
Service-A
TestServiceA.java
#FeignClient( url = "/executeOrder", fallbackFactory = TestServiceAFallback.class )
public interface TestServiceA extends Serializable{
#PostMapping( value = "order/{requestId}/order/{orderId}/{command}" )
public ResponseEntity<ProcessInstanceVariable> executeOrderAction( #PathVariable( name = "command" ) String command );
}
Service-B from where the custom exception is thrown
TestServiceBController.java
#PostMapping( value = /executeOrder )
public ResponseEntity<ProcessInstanceVariable> executeOrderAction( #PathVariable( value = "command" ) String command )
{ //switch code to check the command value and throw exception for one particular command
throw new ValidationException("validation exception from service B");
}
I have an advice also, which handles Validation Exceptions and there is a method like this in that class
TestServiceBControllerAdvice.java
#ExceptionHandler( ValidationException.class )
public ResponseEntity<Object> handleValidationException( ValidationException ve )
{
return new ResponseEntity<>( ve.getMessage(), HttpStatus.BAD_REQUEST );
}
So, I was expecting to receive the message on TestServiceA side which I sent from TestServiceB, but I received a generic message showing that BAD REQUEST while reading the API.
I'm not sure if any extra configuration is required on TestServiceA side apart from below configuration:
testServiceA.properties
feign.hystrix.enabled=true
Let me know if anything is missing from my end, I have gone through this documentation and seems to me I have done the implementation the way it should happen to get the message and body of exception thrown from other service.
For anyone who comes to this question looking for some answers, I did end up implementing ErrorDecoder, which helped me in capturing the errors. The details are a little fade to me, how the message was caught.
But I used the below code:
public class CustomExceptionDecoder implements feign.codec.ErrorDecoder
{
#Override
public Exception decode( String methodKey,
Response response )
{
final ErrorDecoder defaultErrorDecoder = new Default();
try
{
if( response.body() != null )
{
byte[] bodyData = Util.toByteArray( response.body().asInputStream() );
String responseBody = new String( bodyData );
LOGGER.error( "Error captured in Custom Exception Decoder: ", responseBody );
return new CustomValidationException( responseBody );
}
}
catch( IOException e )
{
LOGGER.error( "Throwing IOException :: {}", e.getCause() );
}
return defaultErrorDecoder.decode( methodKey, response );
}
}

How to customize GraphQL query validation error message

I'm implementing query layer on database by using GraphQl and spring boot project to perform CRUD operation on sql database. In GraphQL schema i mentioned some fields to be mandatory and when those fields are not mentioned in query it is returning ValidationError error message in default format with 200 status code.
Error :
{
"data": null,
"errors": [
{
value=StringValue{value='1235'}}]}}]}' is missing required fields '[book_type]' # 'create_book'",
"locations": [
{
"line": 3,
"column": 23,
"sourceName": null
}
],
"description": "argument 'insert' with value value=StringValue{value='1235'}}]}}]}' is missing required fields '[book_type]'",
"validationErrorType": "WrongType",
"queryPath": [
"create_book"
],
"errorType": "ValidationError",
"path": null,
"extensions": null
}
],
"dataPresent": false,
"extensions": null
}
And here is my code with layer architecture pattern
Controller :
#Autowired
private GraphQLServer graphQlServer;
#PostMapping("test")
public ResponseEntity<Object> graphQl(#RequestBody String body){
ExecutionResult response = graphQlServer.execute(body);
return ResponseEntity.ok(response);
}
Service :
#Service
public class GraphQLServer {
#Autowired
private GraphQL graphQl;
public ExecutionResult execute(String query) {
return graphQl.execute(query);
}
}
Config :
#Bean
public GraphQL loadSchema() throws IOException {
File schemaFile = schemaResource.getFile();
TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(schemaFile);
RuntimeWiring wiring = buildRuntimeWiring();
GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(typeRegistry, wiring);
return GraphQL.newGraphQL(schema).build();
}
private RuntimeWiring buildRuntimeWiring() {
return RuntimeWiring.newRuntimeWiring()
.type("Mutation", mutationWiring -> mutationWiring.dataFetcher("create_book", bookDataFetcher))
.build();
}
BookDataFetcher :
#Override
public Map<String, Object> get(DataFetchingEnvironment environment) {
//return data from db by getting Map properties from environment
}
The above code is working as expected but my question here is How to customize the error message? In the error message i would like to mention the status 400 since it is bad request from client
First of all , you should call toSpecification() on ExecutionResult to make sure the response obeys the GraphQL Specification.
By default , there is only one ExecutionResult 's implementation provided by graphql-java which is ExecutionResultImpl , so you can cast ExecutionResult to it in order to use its transform() to update its state.
ExecutionResultImpl internally contains all errors detected by the graphql-java. All of them are in the subclass of GraphQLError which mean you have to cast it to the specific sub-class during customization.
In your case , the subclass is ValidationError and the codes look something like :
#PostMapping("test")
public ResponseEntity<Object> graphQl(#RequestBody String body){
ExecutionResult response = graphQlServer.execute(body);
ExecutionResultImpl responseImpl = (ExecutionResultImpl) response;
List<GraphQLError> customizedErrors = Lists.newArrayList();
for (GraphQLError gqlError : responseImpl.getErrors()) {
//Do your error custmosation here....
GraphQLError customizedError = gqlError;
if (gqlError instanceof ValidationError) {
ValidationError error = (ValidationError) gqlError;
customizedError = new ValidationError(error.getValidationErrorType(), error.getLocations(),
"Customizing some error message blablabla....");
}
customizedErrors.add(customizedError);
}
Map<String, Object> specResponse = responseImpl.transform(b->b.errors(customizedErrors)).toSpecification();
return ResponseEntity.ok(specResponse);
}

Java Swagger not generating service where endpoint response type is List<T>

I am trying to use swagger with java.
Using NSwag studio I am able to generate all my endpoints except one that returns a list of objects.
Here is my action in controller:
#ApiOperation(value = "getAll", nickname = "getAll", responseContainer = "List", response = DiakEntity.class)
#GetMapping("/api/diakok")
#ResponseBody
#PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')")
public List<DiakEntity> GetDiakok() throws Exception
{
ServiceObjectResponse<List<DiakEntity>> request = _diakService.getAll();
if(!request.getIsSuccess())
{
throw new Exception(request.getMessage());
}
return request.getObject();
}
I am using swagger-annotations 1.5.23, springfox-swagger-ui 2.9.2, springfox-swagger2 2.9.2.
If I test from Postman it works.
Also tried like this:
#ApiOperation(value = "getAll", nickname = "getAll")
#ApiResponse(code = 200, responseContainer="List", response=DiakEntity.class, message = "Gets all diak objects")
#GetMapping("/api/diakok")
#ResponseBody
#PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')")
public ResponseEntity<List<DiakEntity>> GetDiakok() throws Exception
{
ServiceObjectResponse<List<DiakEntity>> request = _diakService.getAll();
if(!request.getIsSuccess())
{
throw new Exception(request.getMessage());
}
return new ResponseEntity<>(request.getObject(), HttpStatus.OK);
}
thnx
Please try with the following annotation for swagger.
#ApiOperation(value = "getAll", nickname = "getAll")
#ApiResponse(code = 200, responseContainer="List", response=DiakEntity.class)
At the end I changed my action as below, and it started to work
#ApiOperation(value = "all", nickname = "all")
#PostMapping("/api/diak/all")
#ResponseBody
#PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')")
public List<DiakEntity> GetAll(#RequestBody #Valid RequestDiakByName data) throws Exception
{
ServiceObjectResponse<List<DiakEntity>> request = _diakService.getAll();
if(!request.getIsSuccess())
{
throw new Exception(request.getMessage());
}
return request.getObject();
}

Spring MVC : POST and PUT not working

I am developing Spring MVC 4 Dynamic web module application.
In my application I have simple CRUD operations.
Get requests are working fine but POST and PUT are not working at all.
I am getting this error:
HTTP Status 400 - The request sent by the client was syntactically incorrect.
This is my controller code for GET:
#RequestMapping(value = "/getCustomreById/{id}", method = RequestMethod.GET)
public ResponseEntity<CustomerDetails> getCustomer(
#PathVariable("id") String id) {
System.out.println(id);
if (id != null)
return new ResponseEntity<CustomerDetails>(
serv.getCustomerById(id), HttpStatus.OK);
else
return new ResponseEntity<CustomerDetails>(
serv.getCustomerById("1"), HttpStatus.OK);
}
and for POST :
#RequestMapping(value = "/addCustomer", method = RequestMethod.POST)
public int AddCustomer(#RequestBody CustomerDetails customer) {
return serv.addCustomer(customer);
}
POST Request :
{
"customerName": "Sid",
"customerEmail": "sid#gmail.com",
"customerAddress": [{
"address1": "123 Street",
"address2": " ",
"zipCode": "400065",
"city": "Mumbai",
"state": "Maharashtra",
"country": "India",
"region": "Gateway of India"
}]
}
I read on stackoverflow on this question that I need to add multipart reosolver but even aafter adding that I am getting same error.
Assuming you just need to send int id as response, add #ResponseBody to the method
#RequestMapping(value = "/addCustomer", method = RequestMethod.POST)
#ResponseBody
public int AddCustomer(#RequestBody CustomerDetails customer) {
return serv.addCustomer(customer);
}
Otherwise return ResponseEntity as you are doing for GET
return new ResponseEntity<Integer>(serv.addCustomer(customer), HttpStatus.OK);
Adding #ResponseBody will work for this question

Categories

Resources