Java - Dependency Injection - third party libraries - java

I am trying to learn dependency injection. By example I have the following simple web service client I wrote that talks to a to a web service.
public class UserWebServiceClient
{
private Client client;
public UserWebServiceClient(String username, String password)
{
ClientConfig clientConfig = new DefaultApacheHttpClientConfig();
this.client = ApacheHttpClient.create(clientConfig);
this.client.addFilter(new HTTPBasicAuthFilter(username, password));
}
private WebResource getWebResource()
{
WebResource resource = this.client.resource("http://mywebservice/.......");
return resource;
}
public void createUser(String s) throws StorageAPIException
{
getWebResource().post(...);
}
}
Is this a candidate for dependency injection? Should I be injecting the client?
I don't want to push the complexity of that up to the user. I think Im getting a bit confused about when to use DI.

Yes, if I came across this code I'd change it to be:
public class UserWebServiceClient
{
private Client client;
public UserWebServiceClient(Client client)
{
this.client = client;
}
...
}
Injecting the Client allows me to pass any implementation of Client I choose including mock instances in order to test this class.
Additionally in this case, changing the class in this way also allows the use different implementation of ClientConfig.
In short, the class just became a whole load more reuseable.

It's best to use constructor injection as opposed to field injection. Doing so enables you to swap bindings for testing. It's also good to separate credentials for the same reason.
Your bindings would then be made available via Module or some form of configuration.
With Guice it may look something like this...
public class UserWebServiceClient
{
private Client client;
#Inject
public UserWebServiceClient(Client client)
{
this.client = client;
}
...
}
Your module
public class RemoteModule extends AbstractModule {
public void configure() {
}
#Provides
public Client provideClient(#Named("username") String username,
#Named("password") String password) {
ClientConfig clientConfig = new DefaultApacheHttpClientConfig();
Client client = ApacheHttpClient.create(clientConfig);
client.addFilter(new HTTPBasicAuthFilter(
username, password));
return client;
}
#Provides
#Named("username")
public String provideUsername() {
return "foo";
}
#Provides
#Named("password")
public String providePassword() {
return "bar";
}
}

Related

Spring Cloud Feign Interceptor

I have created a ClientHttpRequestInterceptor that I use to intercept all outgoing RestTemplate requests and responses. I would like to add the interceptor to all outgoing Feign requests/responses. Is there a way to do this?
I know that there is a feign.RequestInterceptor but with this I can only intercept the request and not the response.
There is a class FeignConfiguration that I found in Github that has the ability to add interceptors but I don't know in which maven dependency version it is.
A practical example of how to intercept the response in a Spring Cloud OpenFeign.
Create a custom Client by extending Client.Default as shown below:
public class CustomFeignClient extends Client.Default {
public CustomFeignClient(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier) {
super(sslContextFactory, hostnameVerifier);
}
#Override
public Response execute(Request request, Request.Options options) throws IOException {
Response response = super.execute(request, options);
InputStream bodyStream = response.body().asInputStream();
String responseBody = StreamUtils.copyToString(bodyStream, StandardCharsets.UTF_8);
//TODO do whatever you want with the responseBody - parse and modify it
return response.toBuilder().body(responseBody, StandardCharsets.UTF_8).build();
}
}
Then use the custom Client in a configuration class:
public class FeignClientConfig {
public FeignClientConfig() { }
#Bean
public Client client() {
return new CustomFeignClient(null, null);
}
}
Finally, use the configuration class in a FeignClient:
#FeignClient(name = "api-client", url = "${api.base-url}", configuration = FeignClientConfig.class)
public interface ApiClient {
}
Good luck
If you want to use feign from spring cloud, use org.springframework.cloud:spring-cloud-starter-feign as your dependency coordinates. Currently the only way to modify the response is to implement your own feign.Client.

How do I execute a method in Spring when the Server is ready?

I have the following problem: I'm using Spring-Boot for a little private web-based project and I want Spring to make a call to a webservice when it's started. And by started I mean "when my application is ready to handle requests".
I've already tried implementing the ApplicationListener<ContextRefreshedEvent> but it did not work, as the Event happend to early (i.e. before the embedded server was ready to handle request). Also the options mentioned in this question did not solve this problem.
My question now is: Is there any possibilty to tell Spring to execute something after the server has finished starting up and is ready to handle requests?
EDIT (in response to Daniel's answer):
The problem is that I need some injected properties to make that webservice call, and since injecting static values does not work in spring this approach is no option.
My listener, that does what I want, just a bit too early looks something like this:
#Component
public class StartupListener implements ApplicationListener{
#Autowired
private URLProvider urlProvider;
#Value("${server.port}")
private int port;
#Value("${project.name}")
private String projectName;
#Override
public final void onApplicationEvent(ContextRefreshedEvent event) {
RestTemplate template = new RestTemplate();
String url = uriProvider.getWebserviceUrl(this.projectName);
template.put(url, null);
}
}
SECOND EDIT:
Although this question solves a very similar problem it seems like I'm not able to inject into the object because it needs to have a constructor of the form (org.springframework.boot.SpringApplication, [Ljava.lang.String;).
Also it would be desirebale to solve it without having to create the spring.factories file but by using annotations.
If I understand what your problem is, you could call the webservice on your application main, right after it initiates.
public static void main(String[] args) {
new SpringApplication(Application.class).run(args);
//call the webservice for you to handle...
}
I'm not sure if this is what you want...
In your component you can use the #PostConstruct annotation. e.g.
#Component
public class StartupListener {
#Autowired
private URLProvider urlProvider;
#Value("${server.port}")
private int port;
#Value("${project.name}")
private String projectName;
#PostConstruct
public final void init() {
RestTemplate template = new RestTemplate();
String url = uriProvider.getWebserviceUrl(this.projectName);
template.put(url, null);
}
}
This will fire once the bean has been initialised and autowiring has taken place.
#Component
public class StartUp implements ApplicationListener<WebServerInitializedEvent> {
private WebClient webClient;
#Override
public void onApplicationEvent(WebServerInitializedEvent event) {
String baseUrl = "http://url.com"
webClient = WebClient.create(baseUrl);
executeRestCall(baseUrl+"/info");
}
void executeRestCall(String uri) {
try {
webClient.get()
.uri(uri)
.exchange()
.block();
} catch (Exception e) {
log.error("Request failed for url - {}",uri, e);
}
}}

How to use CXF client in thread safe way

I have created the client stub for below service using apache-cxf's wsdl2java command.
http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL
Then I invoke the getWeatherInformation() method as below.
Weather weatherService = new Weather();
WeatherSoap weatherSoap = weatherService.getWeatherSoap();
ArrayOfWeatherDescription result = weatherSoap.getWeatherInformation();
I have read that cxf clients are thread safe. But I have a doubt whether it is safe to use the same WeatherSoap instance accross multiple threads? Or instead should/can I use an instance of Weather class, accross multiple threads?
Thanks.
EDIT:
What I do is I have exposed a RESTful API to public and if somebody calls that rest service I call another SOAP service. Above code is used to call the SOAP service. What I want to know is should I execute all the above lines for each rest request or can I reuse an instance of Weather or WeatherSoap to serve all the REST requests.
Yes CXF is thread safe, you can use single instance/singleton for Weather and WeatherSoap, you can think of cxf as similar to servlet engine which handles all the infrastructure for you such as transport, databinding for you. I had similar usecase, where I had a front end presentation layer and number of network servers, to interact between these I had a rest for presentation and SOAP which implements business logic as well as interacts with servers. Hence I implemented a soap client in rest layer. I had requirement were I needed split rest request and invoke parallel soap calls which had time delays 800ms. I performance tested the entire setup and did not run-up into any thread issues.
So coming into to client implementation
Pure Java
public class MySoapClient{
private static WeatherSoap weatherSoap;
private MySoapClient(){
}
public static WeatherSoap getClient(){
if(weatherSoap==null){
Weather weatherService = new Weather();
weatherSoap= weatherService.getWeatherSoap();
}
return weatherSoap;
}
}
And I would modify the Weather class to get SOAP url from properties file.
#WebServiceClient(name = "Weather",
wsdlLocation = "classpath:weather.wsdl",
targetNamespace = "http://ws.cdyne.com/WeatherWS/")
public class Weather extends Service {
private static final Logger LOG = LoggerFactory.getLogger(Weather.class);
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName("http://ws.cdyne.com/WeatherWS/", "Weather");
public final static QName WeatherHttpPost = new QName("http://ws.cdyne.com/WeatherWS/", "WeatherHttpPost");
public final static QName WeatherHttpGet = new QName("http://ws.cdyne.com/WeatherWS/", "WeatherHttpGet");
public final static QName WeatherSoap12 = new QName("http://ws.cdyne.com/WeatherWS/", "WeatherSoap12");
public final static QName WeatherSoap = new QName("http://ws.cdyne.com/WeatherWS/", "WeatherSoap");
static {
URL url = null;
try {
url = new URL(MyPropertiesUtil.getProperty("app.weather.url"));
} catch (MalformedURLException e) {
LOG.error(e.getMessage(), e);
}
if (url == null) {
LOG.error("an issue with your url");
}
WSDL_LOCATION = url;
}
public Weather(URL wsdlLocation) {
super(wsdlLocation, SERVICE);
}
public Weather(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
public Weather() {
super(WSDL_LOCATION, SERVICE);
}
//All the other interface methods
}
Using Spring
if you are using spring you can make things even simpler, you can eliminate Weather.java class by using configuration file as shown below and let cxf generate proxy for you.
<jaxws:client id="weatherSoap" serviceClass="com.cdyne.ws.weatherws.WeatherSoap" address="${app.weather.url}" />
And Business Class would look like below.
#Component
MyBusinessLogic{
#Autowired
private WeatherSoap weatherSoap;
public ArrayOfWeatherDescription getOutput(){
return weatherSoap.getWeatherInformation();
}
}

injecting mocks with mockito and powermocks

I was wondering how should I go about injecting a mocks - we have bunch of classes that do server calls, however our CI system can not access external resources and thus will not make a call to a server. Thus, the call has to be simulated and hardcoded values (such as response codes) needed to be return.
So, here is a snippet of a code:
HttpPost httpRequest = new HttPost(uri);
//some code here
try{
httpRequest.setEntity(entity);
HttpResponse response = httpClient.execute(httpRequest);
...
//other, irrelevant, code is here
So, is it possible to inject a mock into httpClient.execute(httpRequest) and return hardcoded response entity from a test unit?
Thank you
Usually mocking some object looks like this:
public class TestClass {
private HttpServer server;
public HttpServer getServer() {
return server;
}
public void setServer(HttpServer server) {
this.server = server;
}
public void method(){
//some action with server
}
}
And test class:
public class TestClassTest {
//class under test
TestClass test = new TestClass();
#org.junit.Test
public void testMethod() throws Exception {
HttpServer mockServer = Mockito.mock(HttpServer.class);
test.setServer(mockServer);
//set up mock, test and verify
}
}
Here you some useful links:
Code example
Official documentation

How to unit test the rest call using easy mock

How to unit test the below method with the EasyMock. I tried to mock WebResource but it is returning me a NullPointerException.
public void connect()
{
Client client = setUpClient();
WebResource jobBuilder = client.resource("URL");
String jobXml = jobBuilder.accept(MediaType.APPLICATION_XML)
.type(MediaType.APPLICATION_JSON)
.entity(request)
.post(String.class);
}
public Client setUpClient()
{
ClientConfig cc = new DefaultClientConfig();
cc.getClasses().add(JacksonJsonProvider.class);
Client client = Client.create(cc);
return client;
}
You clearly have to read up on the Inversion of Control Principle (http://en.wikipedia.org/wiki/Inversion_of_control), if not for the sake of the better designed code, then for the sake of unit testing. The client object in the method above is created inside the method itself, using the static factory method Client.create(). There is no good way to inject a mock collaborator with that approach. You should either allow injection of the client via a setter or a constructor, or delegate its creation to some sort of a factory.
If you use the 1st approach, you can inject the mock client directly via the setter or constructor during the unit test setup.
If you use the 2nd approach, you can provide a factory that would return a mock client when called.
EDIT 5/03:
Here's example of making your code testable by providing an object factory for a 3rd party library object:
public class ClassToTest {
private ClientFactory factory;
public ClassTotest() {
this(new ClientFactory());
}
public ClassToTest(ClientFactory factory) {
this.factory = factory;
}
public void connect() {
Client client = factory.getClient();
WebResource jobBuilder = client.resource("URL");
String jobXml = jobBuilder.accept(MediaType.APPLICATION_XML)
.type(MediaType.APPLICATION_JSON)
.entity(request)
.post(String.class);
}
}
public class ClientFactory() {
public Client getClient() {
ClientConfig cc = new DefaultClientConfig();
cc.getClasses().add(JacksonJsonProvider.class);
Client client = Client.create(cc);
return client;
}
}
Now, in your application code you can instantiate your class using the no-argument constructor. In the unit test you would use the other constructor. This way you would be able to inject a mock Client that you would script for the purpose of testing WebResource.
Hope this helps.

Categories