How to create an "https" connection in an MSF4J microservice? - java

I'm new to MSF4J micorservices. I was trying out a hello-world MSF4J example as given here: https://javahelps.com/wso2-msf4j-hello-world. Everything works as expected, but when I try to access the service using an https url https://localhost:8080/service in the Chrome browser, I am getting an error as This site can’t provide a secure connection. ERR_SSL_PROTOCOL_ERROR.
I'm using Java 8 and my code is this (only two Java classes):
import javax.ws.rs.*;
#Path("/service")
public class HelloService {
#GET
#Path("/")
public String get() {
System.out.println("GET invoked");
return "Hello world!";
}
#POST
#Path("/")
public void post() {
System.out.println("POST invoked");
}
}
public class Main {
public static void main(String[] args) {
new MicroservicesRunner(8080).
deploy(new HelloService())
.start();
}
}
Dependencies in pom.xml:
<dependencies>
<dependency>
<groupId>org.wso2.msf4j</groupId>
<artifactId>msf4j-core</artifactId>
<version>2.5.2</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
Is it possible to enable https connection with this service? if yes, then how can it be done?
I found some https configuration here: https://github.com/wso2/msf4j/blob/master/samples/spring-helloworld/README.md#2-configuring-https-transport . But I am not sure if this configuration method should be used here, as it says "Configuring MSF4J through Spring" in its title and I am not using any Spring libraries.
Any help would be greatly appreciated.

Related

How to decode JSon response with custom Feign client?

In my application, I have to know, from a list a server addresses, which are up. The solution I found is to call health endpoint from Spring-Boot Actuator, for each of them. The JSon reponse is:
{
"status": "UP"
}
In other parts of the application, I use Feign clients from Spring-Cloud defined with the #FeignClient annotation, which works perfectly:
#FeignClient(
name = "tokenProxy",
url = "${host}:${port}"
)
Unfortunately this kind of configuration doesn't allow to re-use the same client to call the same endpoint on different addresses. So I have to define my own custom client (If there is another solution, do not hesitate to tell me! ):
#GetMapping(
value = "/servers"
)
public Server discover() {
MyClient myClient = Feign.builder()
.target(
Target.EmptyTarget.create(
MyClient.class
)
);
return myClient.internalPing(URI.create("http://localhost:8090"));
}
interface MyClient {
#RequestLine("GET /actuator/health")
Server internalPing(URI baseUrl);
}
class Server {
private final String status;
#JsonCreator
public Server(#JsonProperty("status") String status) {
this.status = status;
}
public String getStatus() {
return status;
}
}
When I call the endpoint /servers, I get the following error, indicating that my custom Feign client isn't confgured with the appropriate decoder:
feign.codec.DecodeException: class com.xxx.web.Server is not a type supported by this decoder.
at feign.codec.StringDecoder.decode(StringDecoder.java:34) ~[feign-core-10.10.1.jar:na]
at feign.codec.Decoder$Default.decode(Decoder.java:92) ~[feign-core-10.10.1.jar:na]
at feign.AsyncResponseHandler.decode(AsyncResponseHandler.java:115) ~[feign-core-10.10.1.jar:na]
at feign.AsyncResponseHandler.handleResponse(AsyncResponseHandler.java:87) ~[feign-core-10.10.1.jar:na]
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:138) ~[feign-core-10.10.1.jar:na]
I guess i should use JacksonDecoder, but I cannot find it in my dependencies from Spring-Cloud Hoxton.SR5:
<dependencies>
...
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
...
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencyManagement>
Could someone help me, either with a better solution for my need or a explanation on how to properly configure custom Feign clients?
Thanks in advance
In fact, the library which includes Jackson decoder and encoder was not loaded by default when using spring-cloud dependencies. To fix the issue I simply had to add the following to my pom.xml file:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jackson</artifactId>
</dependency>
Another way could be annotating the class with #Import(FeignClientsConfiguration.class) which is the default configuration provided by Spring Cloud Netflix.
Then it becomes easy to inject both Encoder and Decoder when creating the Feign client:
#Import(FeignClientsConfiguration.class)
#Configuration
public class MyConfiguration {
(...)
Myclient myClient (Decoder feignDecoder, Encoder feignEncoder) {
return Feign.builder()
.decoder( feignDecoder )
.encoder( feignEncoder )
.target(
Target.EmptyTarget.create(
MyClient.class
)
);
}
There are two different defined encoders in the configuration class (pageable or not), so pay attention to clearly identify which you want, either by its name or a qualifier.

How can we start a mockserver from Java to receive incoming traffic?

I am trying to start a mock a server from Java and keep it running to receive incoming requests from other sources (Postman, CURL, etc).
I have tried the Junit approach, but when the unit tests finishes, the server is shutdown.
On the other hand, running the standalone version
http://www.mock-server.com/mock_server/running_mock_server.html#running_from_command_line
keeps the mock server running.
I would like to achieve the same thing, but from the Java code.
The question is, how may I make it run and stay running?
Thanks
So you need an HTTP server for non-testing purposes? I'd try with Spring, something like:
#RestController
public class CatchAllController {
#RequestMapping("/**")
public String catchAll(HttpServletRequest request) {
return request.getRequestURI();
}
}
There is an example on that same page (paragraph "Client API - starting and stopping"). This code works for me:
import static org.mockserver.integration.ClientAndServer.startClientAndServer;
import org.mockserver.integration.ClientAndServer;
public class Checker {
public static void main(String[] args) {
ClientAndServer mockServer = startClientAndServer(1080);
}
}
You have to call
mockServer.stop();
later to stop it.
You will need the following maven dependency:
<!-- mockserver -->
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-netty</artifactId>
<version>5.5.1</version>
</dependency>

Connect to Web-Socket-Server at runtime from back-end java class

I have implemented the following Web-Socket-Server using spring. We don't want to use STOMP and JSocks.
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new WebSocketServerHandler(), "/websocket/user/{userId}");
}
}
#Service
public class WebSocketServerHandler extends TextWebSocketHandler {
private List<WebSocketSession> sessions = new ArrayList<WebSocketSession>();
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message)
throws Exception {
session.sendMessage(message);
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
sessions.add(session);
}
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
sessions = null;
}
}
Now I can successfully connect to that Web-Socket-Server from front-end client (browser). Now I want to connect to that Web-Socket-Server from some of my java class at Run-Time and then want to send messages to that Web-Socket-Server. Anyone have any idea how I can I do that?
I have added a test Rest Controller as below:
#RequestMapping(value = "/web-socket/message/{message}")
public void sendMessage(#PathVariable("message") final String message) throws Exception {
final String WS_URI = "ws://localhost:8080/web-socket/user/23";
final CountDownLatch latch = new CountDownLatch(1);
WebSocketClientHandler handler = new WebSocketClientHandler(latch);
WebSocketClient client = new StandardWebSocketClient();
WebSocketSession session = client.doHandshake(handler, WS_URI).get();
session.sendMessage(new TextMessage(message));
latch.await(5, TimeUnit.SECONDS);
session.close();
}
I am successfully able to send message to the websocket server, only for the 1st time by calling this Test RestController using google rest client. If I need to send message again, then I have to restart the tomcat server. Is there anything wrong what I am doing here?
spring-websocket has WebSocket client support. The basic contract is the WebSocketClient. Spring doesn't implement the entire WebSocket support, but offers an abstraction layer over other implementations. The common Spring client abstraction is the StandardWebSocketClient. A simple example look something like
public static void main(String... args) throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
EchoHandler handler = new EchoHandler(latch);
WebSocketClient client = new StandardWebSocketClient();
WebSocketSession session = client.doHandshake(handler, ECHO_URL).get();
session.sendMessage(new TextMessage("Hello World"));
latch.await(5000, TimeUnit.SECONDS);
session.close();
}
Where EchoHandler is a client side WebSocketHandler (same as server side).
public class EchoHandler extends TextWebSocketHandler {
private final CountDownLatch latch;
public EchoHandler(CountDownLatch latch) {
this.latch = latch;
}
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
System.out.println("------- received client message ------");
System.out.println(message.getPayload());
System.out.println("--------- end client message ---------");
latch.countDown();
}
}
You can also wrap the WebSocketClient in a WebSocketConnectionManager if you are running the client in a String environment. This will give you some lifecycle helpers if you need it. See example in Spring Boot sample.
As far the the dependencies, like I said, Spring doesn't implement the entire WebSocket client support, so you need an implementation. If you are running the client in a server that supports WebSocket, then you will not need to add anything. The support implementation should already be on the classpath from the server. The main supported WebSocket implementations are Jetty, Tomcat, Undertow (mainly Wildfly or standalone Undertow), Tyrus (i.e. Glassfish, WebLogic).
If you are running the client in a standalone app, then you will need to add a WebSocket implementation. Unfortunately, from what I tested, none of the implementation provide a complete workable "client-only" jar. They either require or already pull in the complete (server included) implementation. So using just the client, will still require pulling in a bunch of server jars. Here's what I came up with from testing
Common for all
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>${websocket.version}</version>
</dependency>
Tomcat
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-websocket</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
</dependency>
Undertow
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-websockets-jsr</artifactId>
<version>${undertow.version}</version>
</dependency>
Tyrus
<!-- tyrus-client is pulled in by this. -->
<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-server</artifactId>
<version>${tyrus.version}</version>
</dependency>
Jetty
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-client</artifactId>
<version>${jetty.version}</version>
</dependency>
With Jetty, it does't use the standard Java WebSocket API, so it is not used in the StandardWebSocketClient. You will need to do instead
JettyWebSocketClient client = new JettyWebSocketClient();
client.start();
Everything else above is the same though.
I just looked at the Spring source to see all the different dependencies they used. You can check it out and play around with the dependencies. Maybe there are different more efficient (lighter) combination of dependencies that will still work. The above is just what I tested with and was able to get to work.

Why is ServletTester in Jetty 9.x so slow?

Jetty's ServletTester is pretty useful for testing Servlet apps. I have used Jetty 6's ServletTester before and it worked perfectly.
For example:
Jetty 6.x
pom.xml
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-servlet-tester</artifactId>
<version>6.1.26</version>
<scope>test</scope>
</dependency>
SampleServletTest.java
package example;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.mortbay.jetty.testing.HttpTester;
import org.mortbay.jetty.testing.ServletTester;
public class SampleServletTest {
#Test
public void testDoGet() throws Exception {
ServletTester tester = new ServletTester();
tester.addServlet(SampleServlet.class, "/index");
tester.start();
HttpTester request = new HttpTester();
request.setMethod("GET");
request.setHeader("Host", "tester"); // should be "tester"
request.setURI("/index");
request.setVersion("HTTP/1.1");
request.setContent("");
String responses = tester.getResponses(request.generate());
HttpTester response = new HttpTester();
response.parse(responses);
assertThat(response.getStatus(), is(equalTo(200)));
}
}
Jetty 9.x
ServletTester's APIs are much improved in Jetty 9.x.
pom.xml
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>9.0.7.v20131107</version>
<scope>test</scope>
</dependency>
SampleServletTest.java
package example;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.servlet.ServletTester;
public class SampleServletTest {
#Test
public void testDoGet() throws Exception {
ServletTester tester = new ServletTester();
tester.addServlet(SampleServlet.class, "/index");
tester.start();
HttpTester.Request request = HttpTester.newRequest();
request.setMethod("GET");
request.setHeader("Host", "tester"); // should be "tester"
request.setURI("/index");
request.setVersion("HTTP/1.1");
request.setContent("");
HttpTester.Response response = HttpTester.parseResponse(tester.getResponses(request.generate()));
assertThat(response.getStatus(), is(equalTo(200)));
}
}
New API looks very cool but unfortunately above code runs much slowly... This code takes 10 seconds to run every time. Can you believe it?
Is there anyone who has knowledge of this issue? If this is just my mistake, it's very good news.
This is normal behavior for the request configuration you have.
It has to do with HTTP persistent connections.
Jetty 6's servlet tester defaulted to HTTP/1.0 behavior.
HTTP/1.0 has no official specification for persistent connections, but clients have evolved over the years to adopt a non-persistent behavior, which can be overridden with a negotiated Connection header.
With HTTP/1.0, its 1 request, then 1 response, then connection is closed. Unless the client sends a Connection: Keep-Alive header (and the server responds with the same header)
Jetty 9's HttpTester also defaults to HTTP/1.0.
You specified request.setVersion("HTTP/1.1"); in your example, where all connections are considered persistent unless declared otherwise. So adding Connection: close will force the server to close the connection after the response has been sent, not waiting for another request.
So you have 2 options here:
Stick with HTTP/1.1 and also add request.setHeader("Connection", "close");
or downgrade your HTTP version to 1.0. using either:
request.setVersion("HTTP/1.0");
or comment out the call to request.setVersion(); and rely on default behavior.

How do I register the Jackson provider with the Wink client?

I'm trying to set up a toy application (which may turn in to a real application someday). I'm running into a problem with Wink and Jackson. I've got two applications: one runs wink-server on jetty and seems to be providing some JSON data just fine; one runs wink-client on jetty and receives the JSON data just fine. The problem lies in automagically deserializing the JSON data back into my Java bean.
Here's the code I use in my wink client action:
RestClient client = new RestClient();
Resource resource = client.resource("http://localhost:8081/helloworld");
User user = resource.accept(MediaType.APPLICATION_JSON).get(User.class);
Here's the error I receive when I try to run the Struts action:
java.lang.RuntimeException: No javax.ws.rs.ext.MessageBodyReader found for type class my.package.structure.User and media type application/json. Verify that all entity providers are correctly registered.
org.apache.wink.client.internal.handlers.ClientResponseImpl.readEntity(ClientResponseImpl.java:123)
org.apache.wink.client.internal.handlers.ClientResponseImpl.getEntity(ClientResponseImpl.java:65)
org.apache.wink.client.internal.handlers.ClientResponseImpl.getEntity(ClientResponseImpl.java:52)
org.apache.wink.client.internal.ResourceImpl.invoke(ResourceImpl.java:186)
org.apache.wink.client.internal.ResourceImpl.get(ResourceImpl.java:294)
my.package.structure.action.HelloWorldAction.execute(HelloWorldAction.java:29)
...
If I replace the last line in the first code snippet with the following line, everything works fine and dandy.
String message = resource.accept(MediaType.APPLICATION_JSON).get(String.class);
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(message, User.class);
It's clear that the data is getting across just fine, but the problem seems to lie with the fact that the JacksonJsonProvider class is not registered with Wink client. I've seen a lot of ways to register the provider with the Wink server, but not the Wink client.
Is it possible to do make the first code snippet operate properly? If so, how?
(As an aside, the other problem may be that I'm missing annotations on my User class. Right now there aren't any. Maybe I need some...)
Step 1: Create a class that extends javax.ws.rs.core.Application that allows you to set singletons.
import java.util.Collections;
import java.util.Set;
import javax.ws.rs.core.Application;
public class ClientApplication extends Application {
private Set<Object> singletons = Collections.emptySet();
#Override
public Set<Object> getSingletons() {
return singletons;
}
public void setSingletons(final Set<Object> singletons) {
this.singletons = singletons;
}
}
Step 2: In your action, create a org.apache.wink.client.ClientConfig for your org.apache.wink.client.RestClient. This allows you add the org.codehaus.jackson.jaxrs.JacksonJsonProvider to your providers list.
ClientApplication clientApplication = new ClientApplication();
Set<Object> s = new HashSet<Object>();
s.add(new JacksonJsonProvider());
clientApplication.setSingletons(s);
ClientConfig clientConfig = new ClientConfig().applications(clientApplication);
RestClient restClient = new RestClient(clientConfig);
Step 3: Create the org.apache.wink.client.Resource, use the get(Class<T> responseEntity) method and everything will now work as expected.
Resource resource = client.resource("http://localhost:8081/helloworld");
User user = resource.accept(MediaType.APPLICATION_JSON).get(User.class);
If you want to be really slick about it, you can use Spring to set up a ClientConfig bean and inject it in to your actions. Then, you can just call new RestClient(clientConfig) every time and not worry about replicating the entire setup.
i ran into this issue when trying to write some integration tests that POST an object for my rest plugin.
Rather then spinning out a new class you can provide the Jackson provider with an inline class.
#Before
public void setup(){
javax.ws.rs.core.Application app = new javax.ws.rs.core.Application() {
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(JacksonJaxbJsonProvider.class);
return classes;
}
};
//create auth handler
clientConfig = new ClientConfig();
clientConfig.applications(app);
BasicAuthSecurityHandler basicAuthSecurityHandler = new BasicAuthSecurityHandler();
basicAuthSecurityHandler.setUserName(USERNAME);
basicAuthSecurityHandler.setPassword(PASSWORD);
clientConfig.handlers(basicAuthSecurityHandler);
//create client usin auth and provider
client = new RestClient(clientConfig);
}
Then you can post and consume your annotated objects.
#Test
public void aReadWriteTokenCanBeCreatedAsRequested(){
ClientResponse response = client.resource(resourceUrlToken).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).post(readAndWriteToken);
assertEquals("Could not create token needed for test",200,response.getStatusCode());
readAndWriteToken = response.getEntity(TokenResource.class);
assertNotNull("Returned token does not have UUID",readAndWriteToken.getUuid());
}
If you're using maven you can make sure Jackson is on the test classpath (check for compatible versions):
<!-- TEST DEPENDENCIES -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
<version>1.9.1</version>
<scope>test</scope>
</dependency>
I wish I could help with registration; but with respect to annotations, I don't think you should need any for Jackson to try to deserialize value. If you are missing something you need you would get different kind of exception.

Categories