I am unable to moock webclient
WebClientConfig.java
public #Bean("oauthWebClient") WebClient oauthWebClient() {
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(
HttpClient.create().option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000)
.doOnConnected(connection -> connection.addHandlerLast(new ReadTimeoutHandler(10))
.addHandlerLast(new WriteTimeoutHandler(10)))))
.filter(logOAuthResponse()).baseUrl(oauthTokenUrl)
.defaultHeaders(headers -> {
headers.setBasicAuth(secretHeaderValue);
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
})
.defaultUriVariables(oauthUriVariables)
.build();
}
serviceUti.java
package com.test.stats.volts.busops.utils;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientRequestException;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.test.stats.volts.busops.exception.VoltsDataAccessException;
import com.test.stats.volts.busops.exception.MtnDataAccessNotRespondingException;
import com.test.stats.volts.busops.exception.VoltsDataAcessException;
import com.test.stats.volts.busops.exception.UnableToGetOAuthTokenException;
#Component("voltsDataAccessServiceUtil")
public class ServiceUtil {
private static final Logger logger = LoggerFactory.getLogger(ServiceUtil.class);
#Autowired
#Qualifier("voltsDataAccessWebClient")
WebClient voltsDataAccessClient;
#Autowired
#Qualifier("oauthWebClient")
WebClient oauthClient;
#Value("${volts.dataaccess.end.point.save-meeting}")
private String saveMeetingEndPoint;
#Value("${volts.dataaccess.oauth.header-key.name}")
private String oauthHeaderKeyName;
#Value("${volts.dataccess.oauth.response.token.key-name}")
private String oauthResponseTokenKeyName;
#Value("#{${volts.dataaccess.api.endpoints.default.uri-variables}}")
private Map<String, String> voltsDataAccessUriVariables;
#Autowired
ObjectMapper mapper;
#Value("#{${volts.dataaccess.oauth.uri-variables}}")
private Map<String, String> oauthUriVariables;
#Value("${volts.dataaccess.oauth.scope}")
private String oauthScope;
#Value("${volts.dataaccess.oauth.grant_type}")
private String grantType;
#Value("${volts.dataaccess.oauth.secret.header-name}")
private String secretHeaderName;
#Value("${volts.dataaccess.oauth.secret.header-value}")
private String secretHeaderValue;
#Value("${volts.dataaccess.end.point.fetch-meeting}")
private String fetchMeetingEndPoint;
#Value("${volts.dataaccess.query.param.api-key-value}")
private String apiKey;
#Retryable(maxAttempts = 2)
public String postOAuthToken() throws VoltsDataAccessException {
try {
String respEntity = oauthClient.post()
.uri(uriBuilder -> uriBuilder.queryParam("scope", oauthScope).queryParam("grant_type", grantType)
.build())
.headers(headers -> headers.setBasicAuth(secretHeaderValue))
.retrieve()
.bodyToMono(String.class)
.block();
if (respEntity == null)
throw new UnableToGetOAuthTokenException(
"Exception while fetching OAUthToken as response from OAUth service is null resposne body");
try {
return mapper.readValue(respEntity, ObjectNode.class).get(oauthResponseTokenKeyName).asText();
} catch (JsonProcessingException e) {
logger.error("Exception while pasring token from Oauth ", e);
throw new UnableToGetOAuthTokenException(
"Exception while fetching OAUthToken as response from OAUth service is null resposne body");
}
} catch (WebClientRequestException e) {
logger.error("Request Exception to OAUth: message {}", e.getMessage(), e);
logger.error("", e);
throw new VoltsDataAccessException(e.getMessage(), HttpStatus.REQUEST_TIMEOUT.value());
}
catch (WebClientResponseException e) {
throw new VoltsDataAccessException(e.getMessage(), e.getRawStatusCode());
}
}
#Retryable(value = VoltsDataAcessException.class)
public ResponseEntity<VoltsResp> postSaveMeetingDetails(SaveVoltsReq voltsReq)
throws VoltsDataAcessException {
try {
ResponseEntity<VoltsResp> resp = voltsDataAccessClient.post()
.uri(saveMeetingEndPoint, uri -> uri.queryParam("apikey", apiKey).build())
.headers(headers -> headers.setBearerAuth(postOAuthToken()))
.contentType(MediaType.APPLICATION_JSON).bodyValue(voltsReq).retrieve()
.toEntity(VoltsResp.class).block();
return resp;
} catch (WebClientRequestException e) {
throw new VoltsDataAcessException(e.getMessage(), HttpStatus.REQUEST_TIMEOUT.value());
} catch (WebClientResponseException e) {
throw new VoltsDataAcessException(e.getMessage(), e.getRawStatusCode());
}
}
}
My test classs
WebClienrTests.java
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.reactive.function.client.WebClient;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.test.stats.volts.busops.config.WebClientConfig;
import com.test.stats.volts.busops.utils.ServiceUtil;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
#SpringBootTest(classes = { ServiceUtil.class, WebClientConfig.class,
ObjectMapper.class })
#RunWith(SpringRunner.class)
class ServiceUtilTests {
public static MockWebServer mockServer = new MockWebServer();
#MockBean
WebClientConfig webClientCondfig;
#Autowired
ServiceUtil serviceUtil;
#MockBean(name = "oauthWebClient")
WebClient oauthClient;
#MockBean(name = "voltsDataAccessWebClient")
WebClient voltsDataAccessWebClient;
#BeforeEach
void startMockServer() throws Exception {
mockServer.start(9090);
oauthClient = WebClient.create(mockServer.url("/").url().toString());
}
#AfterEach
void stopMockServer() throws Exception {
mockServer.shutdown();
}
#Test
void testPostOAuth() throws Exception {
mockServer.enqueue(new MockResponse().setResponseCode(200).setBody("OAuthToken"));
String expected = serviceUtil.postOAuthToken();
assertThat(expected).isNotNull().isEqualTo("OAuthToken");
}
}
When I run above test I get this error
java.lang.NullPointerException:
At postAuthToken() while setting headers .headers(headers -> headers.setBasicAuth(secretHeaderValue))
Please help on how to mock whole thing
Related
I want to send data from Boot(Server) to client!
I am using stomp websocket!
WebSocketClient doesn't work with error that the HTTP response from the server [200] did not permit the HTTP upgrade to WebSocket!
It's my Java Stomp WebSocket client code!
In this code, I tried to config handShakeHeader.... but it doesn't work!
package refill.station.websocket;
import org.junit.jupiter.api.Test;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.simp.stomp.StompHeaders;
import org.springframework.messaging.simp.stomp.StompSession;
import org.springframework.messaging.simp.stomp.StompSessionHandler;
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;
import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;
import org.springframework.web.socket.WebSocketHttpHeaders;
import org.springframework.web.socket.client.WebSocketClient;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.messaging.WebSocketStompClient;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class WebSocketTest {
#Test
public void WebSocketTest() {
WebSocketClient webSocketClient = new StandardWebSocketClient();
WebSocketStompClient stompClient = new WebSocketStompClient(webSocketClient);
stompClient.setMessageConverter(new MappingJackson2MessageConverter());
stompClient.setTaskScheduler(new ConcurrentTaskScheduler());
String url = "ws://localhost:8080/ws/websocket";
StompSessionHandler sessionHandler = new StompSessionHandlerAdapter() {};
List<String> protocols = new ArrayList<>();
protocols.add("v10.stomp");
protocols.add("v11.stomp");
WebSocketHttpHeaders handSahkeHeaders = new WebSocketHttpHeaders();
handSahkeHeaders.setUpgrade("websocket");
handSahkeHeaders.setConnection("Upgrade");
handSahkeHeaders.setSecWebSocketVersion("13");
handSahkeHeaders.setSecWebSocketKey("4hb9p5/JjeKLed5aXlBqnw==");
handSahkeHeaders.setSecWebSocketProtocol(protocols);
StompHeaders stompHeaders = new StompHeaders();
StompSession stompSession = null;
try {
stompSession = stompClient.connect(URI.create(url), handSahkeHeaders, stompHeaders, sessionHandler).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
stompSession.send("/app/control/complete", "msg");
// StompSession stompSession = stompClient.connect(url, sessionHandler).get();
}
}
I checked my WebSocket server with javascript and it works correctly!
This is my WebSocket server configuration!
package refill.station.config.websocket;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.RequestUpgradeStrategy;
import org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrategy;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
RequestUpgradeStrategy upgradeStrategy = new TomcatRequestUpgradeStrategy();
registry.addEndpoint("/ws")
.withSockJS();
registry.addEndpoint("/ws")
.setHandshakeHandler(new DefaultHandshakeHandler(upgradeStrategy))
.setAllowedOrigins("*");
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/control");
}
}
last, it's my controller for websocket!
package refill.station.controller.websocket;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
#RequiredArgsConstructor
#RestController
#Slf4j
public class RefillerCommunicateController {
#MessageMapping("/control/complete")
#SendTo("/control/operate")
public String test(#Payload String data) {
log.info("RefillerCommunicateController-test method");
return "test";
}
}
Trying to write very simple http message converter to add encryption around request & respose.
Implemented rest controller and messageConverter. writeInternal does get invoked during response. However readInternal does not get invoked when request is sent to the rest end point.
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.techm.bm.util.EncryptDecryptUtil;
#Component
public class MessageEncryptionConverter extends AbstractHttpMessageConverter<Object> {
#Autowired
private ObjectMapper objectMapper;
public MessageEncryptionConverter() {
super(MediaType.ALL);
}
#Override
protected boolean supports(Class<?> clazz) {
return true;
}
#Override
protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
return objectMapper.readValue(decrypt(inputMessage.getBody()), clazz);
}
#Override
protected void writeInternal(Object o, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
outputMessage.getBody().write(encrypt(objectMapper.writeValueAsBytes(o)));
}
The below code is for encryption and decryption of request and response body For overall Project to modify response body and request parameters. Please check this code is works for me
For Request Encryption refer readInternal
below is the front-end or postman request sending, you have to add encrypted request in Json formate
Ex.{data : "+++++++++++++YOUR_ENCRYPTED_STRING++++++++++"}
For Response Descryption refer writeInternal
sending back to your front-end or postman request response with encrypted string
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.techm.bm.util.EncryptDecryptUtil;
#Component
public class MessageEncryptionConverter extends AbstractHttpMessageConverter<Object> {
#Autowired
private ObjectMapper objectMapper;
public MessageEncryptionConverter() {
super(MediaType.ALL);
}
#Override
protected boolean supports(Class<?> clazz) {
return true;
}
#Override
protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
return objectMapper.readValue(decrypt(inputMessage.getBody()), clazz);
}
#Override
protected void writeInternal(Object o, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
outputMessage.getBody().write(encrypt(objectMapper.writeValueAsBytes(o)));
}
// Convert request to input stream for sending to the controller
private InputStream decrypt(InputStream inputStream) throws IOException {
//this is API request params
Writer writer = new StringWriter();
char[] buffer = new char[1024];
try {
Reader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
} catch (UnsupportedEncodingException e) {
logger.error("UnsupportedEncodingException" + e.getMessage());
} catch (IOException e) {
logger.error("IOException" + e.getMessage());
} finally {
inputStream.close();
}
try {
JSONObject requestJsonObject = new JSONObject(writer.toString().replace("\n", ""));
// Add your decryption method the object data will get your encrypted string data and return with string of your JSON object
String decryptRequestString = EncryptionUtils.decryptText(requestJsonObject.getString("data"));
if (decryptRequestString != null) {
return new ByteArrayInputStream(decryptRequestString.getBytes(StandardCharsets.UTF_8));
} else {
return inputStream;
}
} catch (JSONException err) {
logger.error("Error" + err.toString());
} catch (NoSuchAlgorithmException | BadPaddingException | DigestException | InvalidKeyException
| InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException e) {
logger.info("NoSuchAlgorithmException | BadPaddingException | DigestException | InvalidKeyException | " +
"InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException:: " + e.getMessage());
}
return inputStream;
}
private byte[] encrypt(Object obj) throws JsonProcessingException {
// do your encryption here
try {
String stringFromObject = objectMapper.writeValueAsString(obj);
// Add your encryption method
String encrypt = EncryptionUtils.encryptText(stringFromObject);
return objectMapper.writeValueAsBytes(encrypt);
} catch (Exception e) {
logger.info("Error While Encrypt data :: " + e.getMessage());
return objectMapper.writeValueAsBytes(obj);
}
}
}
Reference code Spring Boot - Encrypt JSON data
Hope this code is working for you
It is invoking if the request has body and "supports" method return ture
I am new to Junit. I am literally struggling to write Junit test cases for my code. I am working with Spring boot, Batch and JPA. I configured Jobs,file read and write in to DB everything working fine. But when it comes to JUnit, I don't even get an idea to write code. Can anyone help me. Below is my code
FileProcessController.java
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.sc.batchservice.model.StatusResponse;
import lombok.extern.slf4j.Slf4j;
#Slf4j
#RestController
#RequestMapping("/fileProcess")
public class FileProcessController {
#Autowired
private JobLauncher jobLauncher;
#Autowired
#Qualifier("euronetFileParseJob")
private Job job;
#GetMapping(path = "/process")
public #ResponseBody StatusResponse process() throws Exception {
try {
Map<String, JobParameter> parameters = new HashMap<>();
parameters.put("date", new JobParameter(new Date()));
jobLauncher.run(job, new JobParameters(parameters));
return new StatusResponse(true);
} catch (Exception e) {
log.error("Exception", e);
Throwable rootException = ExceptionUtils.getRootCause(e);
String errMessage = rootException.getMessage();
log.info("Root cause is instance of JobInstanceAlreadyCompleteException --> "+(rootException instanceof JobInstanceAlreadyCompleteException));
if(rootException instanceof JobInstanceAlreadyCompleteException){
log.info(errMessage);
return new StatusResponse(false, "This job has been completed already!");
} else{
throw e;
}
}
}
}
BatchConfig.java
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import com.sc.batchservice.model.DetailsDTO;
#Configuration
#EnableBatchProcessing
public class BatchConfig {
private JobBuilderFactory jobBuilderFactory;
#Autowired
public void setJobBuilderFactory(JobBuilderFactory jobBuilderFactory) {
this.jobBuilderFactory = jobBuilderFactory;
}
#Autowired
StepBuilderFactory stepBuilderFactory;
#Value("file:${input.files.location}${input.file.pattern}")
private Resource[] fileInputs;
#Value("${euronet.file.column.names}")
private String filecolumnNames;
#Value("${euronet.file.column.lengths}")
private String fileColumnLengths;
#Value("${input.files.location}")
private String inputFileLocation;
#Value("${input.file.pattern}")
private String inputFilePattern;
#Autowired
FlatFileWriter flatFileWriter;
#Bean
public Job euronetFileParseJob() {
return jobBuilderFactory.get("euronetFileParseJob")
.incrementer(new RunIdIncrementer())
.start(fileStep())
.build();
}
public Step fileStep() {
return stepBuilderFactory.get("fileStep")
.<DetailsDTO, DetailsDTO>chunk(10)
.reader(new FlatFileReader(fileInputs, filecolumnNames, fileColumnLengths))
.writer(flatFileWriter)
.build();
}
}
FlatFileReader.java
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.MultiResourceItemReader;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.FixedLengthTokenizer;
import org.springframework.core.io.Resource;
import com.sc.batchservice.model.DetailsDTO;
import com.sc.batchservice.util.CommonUtil;
import lombok.extern.slf4j.Slf4j;
#Slf4j
public class FlatFileReader extends MultiResourceItemReader<DetailsDTO> {
public EuronetFlatFileReader(Resource[] fileInputs, String filecolumnNames, String fileColumnLengths) {
setResources(fileInputs);
setDelegate(reader(filecolumnNames, fileColumnLengths));
setStrict(true);
}
private FlatFileItemReader<DetailsDTO> reader(String filecolumnNames, String fileColumnLengths) {
FlatFileItemReader<DetailsDTO> flatFileItemReader = new FlatFileItemReader<>();
FixedLengthTokenizer tokenizer = CommonUtil.fixedLengthTokenizer(filecolumnNames, fileColumnLengths);
FieldSetMapper<DetailsDTO> mapper = createMapper();
DefaultLineMapper<DetailsDTO> lineMapper = new DefaultLineMapper<>();
lineMapper.setLineTokenizer(tokenizer);
lineMapper.setFieldSetMapper(mapper);
flatFileItemReader.setLineMapper(lineMapper);
return flatFileItemReader;
}
/*
* Mapping column data to DTO
*/
private FieldSetMapper<DetailsDTO> createMapper() {
BeanWrapperFieldSetMapper<DetailsDTO> mapper = new BeanWrapperFieldSetMapper<>();
try {
mapper.setTargetType(DetailsDTO.class);
} catch(Exception e) {
log.error("Exception in mapping column data to dto ", e);
}
return mapper;
}
}
I have Writer,Entity and model classes also, But if any example or idea upto this code, I can proceed with those classes.
I'am Ropi, i have an issue with my spring boot project test.
The issue is :
UnknownClassName.initializationError : NoClassDefFound UnknownClassName;
when i run the project it with skiped test proccess, it run well . and then i run the test it also run well..
but after i edit the test ( for example : i only add space on my test code), it give me an error.
and then when i edited my working code ( for example : i only add space on my coding work ), it give me illegal error.
My testing Code :
import com.emerio.rnd.otp.service.OtpService;
import org.hamcrest.core.IsNot;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.hamcrest.Matchers.*;
#RunWith(SpringRunner.class)
// #SpringBootTest
#WebFluxTest
public class OtpApplicationTests {
private Integer OTP1;
private Integer OTP2;
#Autowired
private WebTestClient webTestClient;
private OtpService otpService = new OtpService();
#Before
public void setup() throws Exception {
OTP1 = otpService.generateOTP("admin");
OTP2 = otpService.generateOTP("admin1");
}
// #WebFluxTest(OtpController.class)
#Test
public void generateOTPSuccess() throws Exception
{
webTestClient.get().uri("/generateotp/admin")
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.data.otp").isNotEmpty();
}
#Test
public void generateOTPUsernameNotExist() throws Exception
{
webTestClient.get().uri("/generateotp/null")
.exchange()
.expectStatus().isBadRequest();
}
// #Test
// public void generateNewOTP() throws Exception
// {
// webTestClient.get().uri("/generateotp/admin1")
// .exchange()
// .expectStatus().isOk()
// .expectBody()
// .jsonPath("$.[otp != "+OTP2+"]");
// }
#Test
public void validateOTPSuccess() throws Exception
{
webTestClient.get().uri("/validateotp/admin/"+OTP1.toString())
.exchange()
.expectStatus().isOk();
}
#Test
public void validateOTPfailed() throws Exception
{
webTestClient.get().uri("/validateotp/admin/981981")
.exchange()
.expectStatus().isBadRequest();
}
#Test
public void validateOTPWithChar() throws Exception
{
webTestClient.get().uri("/validateotp/admin/AS1213")
.exchange()
.expectStatus().isBadRequest();
}
#Test
public void validateOTPTimeOut() throws Exception
{
// Thread.sleep(300010);
webTestClient.get().uri("/validateotp/admin/981981")
.exchange()
.expectStatus().isBadRequest();
}
My Controller Working code
My Controller :
import com.emerio.rnd.otp.response.Response;
import com.emerio.rnd.otp.service.OtpService;
import com.google.gson.Gson;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
#RestController
public class OtpController
{
private OtpService otpService = new OtpService();
private Response response = new Response();
#RequestMapping(method=RequestMethod.GET , value="/generateotp/{username}", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
public Mono<Object> generateOTP (#PathVariable String username)
{
try
{
if (username.equals("null"))
{
return Mono.just(new Throwable("error"));
} else
{
int otp = otpService.generateOTP(username);
JSONObject jObj = new JSONObject();
jObj.put("otp", otp);
return Mono.just(response.loaded(new Gson().fromJson(jObj.toString(), Object.class)));
}
} catch (Exception e)
{
return null;
}
}
#RequestMapping(method=RequestMethod.GET , value="/validateotp/{username}/{otp}", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
public Mono<Response> validateOTP (#PathVariable String username, #PathVariable String otp)
{
try
{
Integer otpCache = otpService.getOTP(username);
if (!otpCache.toString().equals(otp))
{
System.out.println("cache awal : " + otpCache);
System.out.println("cache : " + otp);
return Mono.just(response.loaded("otp not valid"));
} else
{
otpService.clearOTP(username);
return Mono.just(response.loaded("otp valid"));
}
} catch (Exception e)
{
return null;
}
}
My OTP Service :
package com.emerio.rnd.otp.service;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.springframework.stereotype.Service;
#Service
public class OtpService
{
private static final Integer EXPIRE_MINS = 5;
private LoadingCache<String, Integer> otpCache;
public OtpService()
{
super();
otpCache = CacheBuilder.newBuilder()
.expireAfterWrite(EXPIRE_MINS, TimeUnit.MINUTES).build(new CacheLoader<String,Integer>()
{
public Integer load (String key)
{
return 0;
}
});
}
public int generateOTP (String key)
{
Random random = new Random();
int otp = 100000 + random.nextInt(900000);
otpCache.put(key, otp);
return otp;
}
public int getOTP (String key)
{
try
{
return otpCache.get(key);
} catch (Exception e)
{
return 0;
}
}
public void clearOTP (String key)
{
otpCache.invalidate(key);
}
}
I am trying to set timeout and SSL (https) for WS call:
PS: No need to mark this as duplicated, the only similar question has never been answered.
I tried HttpsUrlConnectionMessageSender that adds support for (self-signed) HTTPS certificates but it does support timeout.
when I switch to HttpComponentsMessageSender that supports timeout (Connection and read timeouts) it does support SSL.
I want to combile timeout and ssl to when calling WS:
webServiceTemplate.setDefaultUri(uri);
response = webServiceTemplate.marshalSendAndReceive(inputs, new SoapHandler(createCredentials(), soapAction));
Finally, did it using HttpComponentsMessageSender. Here is my code:
HttpComponentsMessageSender messageSender = new HttpComponentsMessageSender();
HttpClient httpClient = HttpClientFactory.getHttpsClient(sslUtils, timeout);
messageSender.setHttpClient(httpClient);
webServiceTemplate.setMessageSender(messageSender);
I also created a new factory class HttpClientFactory that sets the SSL and timeout:
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
public class HttpClientFactory {
private static CloseableHttpClient client;
private HttpClientFactory() {
}
public static HttpClient getHttpsClient(SslUtils sslUtils, int timeout) throws Exception {
if (client != null) {
return client;
}
SSLContext sslcontext = getSSLContext(sslUtils);
SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext, new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
HttpClientBuilder httpClientBuilder = HttpClients.custom();
httpClientBuilder.addInterceptorFirst(new ContentLengthHeaderRemover());
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(timeout)
.setConnectionRequestTimeout(timeout)
.setSocketTimeout(timeout)
.build();
return httpClientBuilder.setSSLSocketFactory(factory)
.setDefaultRequestConfig(config)
.build();
}
private static class ContentLengthHeaderRemover implements HttpRequestInterceptor {
#Override
public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
request.removeHeaders(HTTP.CONTENT_LEN);
}
}
public static void releaseInstance() {
client = null;
}
private static SSLContext getSSLContext(SslUtils sslUtils) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, KeyManagementException {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(sslUtils.getKeystore().getInputStream(), sslUtils.getKeyPwd().toCharArray());
sslUtils.getKeystore().getInputStream().close();
KeyStore ts = KeyStore.getInstance("JKS");
ts.load(sslUtils.getTrustStore().getInputStream(), sslUtils.getTrustPwd().toCharArray());
sslUtils.getTrustStore().getInputStream().close();
SSLContextBuilder sslContextBuilder = SSLContexts.custom();
try {
sslContextBuilder = SSLContexts.custom().loadKeyMaterial(ks, ssl.getKeyPwd().toCharArray());
} catch (UnrecoverableKeyException e) {
e.printStack();
}
sslContextBuilder.loadTrustMaterial(ts, new TrustSelfSignedStrategy());
return sslContextBuilder.build();
}
}
For information the SslUtils is just a bean class that holds the keystore and truststore informations' :
public class SslUtils {
private Resource keystore;
private String keyPwd;
private Resource trustStore;
private String trustPwd;
// Getters and Setters
}
This works for me and let me use both SSL and timeout at the same. I hope this will help others.
In a case of HTTPS protocol with basic authentication, you may not need a certificate, you can set the encoded username:password into the header of the request
package com.james.medici.app.ws;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.transport.http.HttpUrlConnectionMessageSender;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.Base64;
#Slf4j
#Configuration
public class SoapClientConfiguration {
#Value("${james.medici.url}")
private String defaultUri;
#Value("${james.medici.username}")
private String userName;
#Value("${james.medici.passcode}")
private String userPassword;
public static final String SEPARATOR = ":";
public static final String AUTHORIZATION = "Authorization";
public static final String BASIC = "Basic ";
class CustomHttpUrlConnectionMessageSender extends HttpUrlConnectionMessageSender {
#Override
protected void prepareConnection(HttpURLConnection connection) throws IOException {
Base64.Encoder enc = Base64.getEncoder();
String userpassword = StringUtils.joinWith(SEPARATOR, userName, userPassword);
String encodedAuthorization = enc.encodeToString(userpassword.getBytes());
connection.setRequestProperty(AUTHORIZATION, BASIC + encodedAuthorization);
super.prepareConnection(connection);
}
}
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.james.medici.app.ws.model");
return marshaller;
}
#Bean
public WebServiceTemplate webServiceTemplate() {
log.info(defaultUri);
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
webServiceTemplate.setMarshaller(marshaller());
webServiceTemplate.setUnmarshaller(marshaller());
webServiceTemplate.setDefaultUri(defaultUri);
webServiceTemplate.setMessageSender(new CustomHttpUrlConnectionMessageSender());
return webServiceTemplate;
}
}