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.
Related
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.
I'm using Netflix' Hystrix libraries to act as a circuit breaker when connecting to remote services in a REST client I am building. I would like to setup the event streaming and dashboard monitoring via the libraries they provide. Looking at their example application here, it appears that I need to apply their servlet filters and servlet classes to my web application.
I'm using Spring Boot with Jersey 2 and wiring up my resources and filters in a JerseyConfig.java (no web.xml). I know that Jersey Filters are not the same as Servlet Filters and am struggling to integrate the two together.
So, how do you take a Java Servlet Filter and make it work as a Jersey Filter and how do you take a Java Servlet and make it work as a Jersey Resource?
My current strategy for the Servlets is to wrap them like so. One for each.
#Path("/hystrix.stream")
public class HystrixResource extends HystrixUtilizationSseServlet {
#Context
HttpServletRequest httpRequest;
#Context
HttpServletResponse httpResponse;
//This returns void because it is a text/stream output that must remain open,
//so the httpResponse is continually written to until the conenction is closed
#GET
public void doGet() throws ServletException, IOException {
doGet(httpRequest, httpResponse);
}
}
This might be working, but the data is basically empty for some reason. I am guessing that reason is because the Filters are not working.
data: {"type":"HystrixUtilization","commands":{},"threadpools":{}}
It is less clear to me how to wrap the Servlet Filters because they expect different inputs and outputs than a Jersey ContainerRequestFilter. The following implementation in my JerseyConfig seems to do nothing because the logs are not indicating that the filters are being registered and I cannot break on lines in these files in debug mode.
#Component
#ApplicationPath("/")
public class JerseyConfig extends ResourceConfig {
private static final Logger LOGGER = Logger.getLogger("JerseyConfig");
public JerseyConfig(){
//filter to provide a bridge between JAX-RS and Spring request attributes
register(RequestContextFilter.class);
register(SpringComponentProvider.class);
//handles custom serialization
register(new ObjectMapperContextResolver());
//try to register the filters - which doesn't work because these aren't Jersey Filters
register(HystrixRequestContextServletFilter.class);
register(HystrixRequestLogViaResponseHeaderServletFilter.class);
registerResources();
/*
* Enable the logging filter to see the HTTP response for each request.
*/
register(new LoggingFilter(LOGGER, true));
}
}
Servlets and Servlet filters should not be registered in the Jersey config. They will simply be ignored. You should instead be registering them with Spring Boot with ServletRegistrationBeans and FilterRegistrationBeans.
In you Spring configuration, you can do something like
#Bean
public ServletRegistrationBean someServlet() {
ServletRegistrationBean registration = ServletRegisrationBean(
new HystrixMetricsStreamServlet(), "/hystrix.stream");
registration.setName("HystrixMetricsStreamServlet");
return registration;
}
#Bean
public FilterRegistrationBean someFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new HystrixRequestContextServletFilter());
registration.setUrlPatterns(Arrays.asList("/*"));
registration.setName("HystrixRequestContextServletFilter");
// you can also set the order of filters if you need to
return registration;
}
Also:
you don't need to register the SpringComponentProvider. This is automatically registered.
If you get a 404 on trying to access the servlet being registered this way, it will be because you are using the default Jersey mapping /*, which hogs up all the request. You can change the mapping or register Jersey as a filter to forward not found requests. See this post
An alternative route, and the one I ended up eventually going with, is to use the Spring cloud/boot starters if you're in a Spring Boot project. This prevented me from having to explicitly define beans and filters as shown in the other answer. Eventually basically worked out of the box.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<exclusions>
<!--We're running our Jersey server w/ Jackson 2. This import uses Jackson 1.x and creates a breaking conflict.-->
<exclusion>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Reference Circuit Breaker getting started guide. The one issue I faced was the Jackson 1 vs Jackson 2 conflict and was able to add the library exclusion. I basically had the Hystrix library jar before, but nothing wired up to make it work.
I'd like to get JAXRS to push all json details for a specific class property into a JsonObject object.
Let's say I have the following class:
public class MyMessage implements Serializable {
private PayloadType payloadType;
private JsonObject payload;
}
REST method of:
#POST
#Path("/send")
#Consumes(MediaType.APPLICATION_JSON)
public Response send(MyMessage message)
I'd like to POST the following JSON, but have the payload property set as a javax.json.JsonObject object.
{
payloadType:'MESSAGE',
payload:{
subject:"My Subject",
body:"This is a message"
}
}
I'm running on Glassfish, so I was expecting that message reader for JsonObject were included with org.glassfish.jersey.media, which is support to be included in the GF4.1. Add the following maven dependency just causes ambiguous class exceptions.
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-processing</artifactId>
<version>2.22.1</version>
</dependency>
So there are a couple things stopping you here.
javax.json (or JSONP) does not handle POJOs. It only handles the javax.json API. If you what you are expecting is that the original provider handle the POJO, while the JSONP provider handle the javax.json, it doesn't work like that. Either you will use the one that handles the POJOs (which doesn't know javax.json or you use the one that handles javax.json. (We do make this happen below though :-)
Glassfish's default provider is MOXy. So we need to disable to to use any other provider. To disable MOXy, you need to set this property
ServerProperties.MOXY_JSON_FEATURE_DISABLE
to true. Either in your web.xml or your Java config.
So to make this work, we should make sure that we are using Jackson. Jackson has the jackson-datatype-jsr353 module which allows to do exactly what you are trying to do, javax.json in POJO.
Glassfish has the Jackson provider already, but you should add it anyway in a provided scope. So add these two dependencies
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.10.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr353</artifactId>
<version>2.3.3</version>
</dependency>
If yoo pay attention to the versions, I am using the same versions that are included in Glassfish 4.1. It uses Jersey 2.10.4 and Jackson 2.3.3. You want the versions to conflict. Even though the jersey-media-json-jackson is provided, it's still a good idea to try and use the same version as the server, when compiling.
Also you should register the JacksonFeature with your application.
And the last thing we need is to register the JSR353Module so that we can get the javax.json support with Jackson. To do that just register the following provider with your application.
#Provider
public class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public ObjectMapperProvider() {
mapper = new ObjectMapper();
mapper.registerModule(new JSR353Module());
}
#Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}
That's it. I've tested it, and it should work.
I am trying to save data on my database with a web service POST wich serializes a HTML form to save a object. The rest client firefox says this:
"The server refused this request because the requested entity is in a format not supported by the requested resource for the requested method"
The eclipse console shows the message:
org.jasig.cas.client.util.CommonUtils - safeGetParameter called on a POST HttpServletRequest for LogoutRequest. Cannot complete check safely. Reverting to standard behavior for this Parameter
I understand that the object that i want to save is not valid, but I don't see what the problem is.
#RequestMapping(value="/solicitudCita", method = RequestMethod.POST)
public #ResponseBody String putSolicitud(#ModelAttribute("Solicitud") Solicitud solicitud) throws Exception{
System.out.println(solicitud.toString());
solicitudCitaAppMService.createOrUpdate(solicitud);
String solicitudAdded = "Solicitud de cita -> {" + solicitud.toString() + "} añadida";
System.out.println(solicitud);
return solicitudAdded;
}
Help me please
Thanks
If you want to call this controller in a RESTful manner, you have to annotate the solicitud parameter as #RequestBody. Second, you have to have the Jackson libraries in you classpath so Spring can pick them up and use them for unmarshalling the object.
If you use Maven, use these dependencies:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.12</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.12</version>
</dependency>
BTW, why do you have to serialize the HTML form and sent it across? I would suggest you use a REST client, for instance this one, that is available in the Chrome WebStore.
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.