We have Rest services implemented using Jersy,my question is when invoking some soap service from our rest implementation, we are creating object for delegate like below,
#POST
#Path("/forgotuserid/validate/mobilenumber")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public ServiceResponse validateMobileNumber(CommunicationDTO commonDTO)
throws ApplicationException, Exception {
ChMTYWebservicesProvidersWsMTY service = new ChMTYWebservicesProvidersWsMTY();
WsMTYPortType portType = service.getChMTYWebservicesProvidersWsMTYPort();
//TODO : other stuffs go here
return response;
}
is there any way to avoid new object creation and have single here?
If you are using Spring framework then there is an option Dependency Injection , You can use that feature.
You can code something like this:
public class SoapWSUtil{
private static WsMTYPortType type;
static {
type = (new ChMTYWebservicesProvidersWsMTY()).getChMTYWebservicesProvidersWsMTYPort();
}
public static WsMTYPortType getType(){
return type;
}
}
And then use it as SoapWSUtil.getType(). It will be thread safe in case, if you won't add state to SoapWSUtil
Related
I'm trying to build a simple app that calls an API with quarkus-rest-client.
I have to inject an API Key as a header which is the same for all resources of the API.
So I would like to put the value of this API Key (that depends on the environment dev/qa/prod) in the application.properties file located in src/main/resources.
I tried different ways to achieve this:
Use directly com.acme.Configuration.getKey into #ClientHeaderParam value property
Create a StoresClientHeadersFactory class which implements ClientHeadersFactory interface to inject the configuration
Finally, I found the way described below to make it work.
My question is: Is there a better way to do it?
Here's my code:
StoreService.java which is my client to reach the API
#Path("/stores")
#RegisterRestClient
#ClientHeaderParam(name = "ApiKey", value = "{com.acme.Configuration.getStoresApiKey}")
public interface StoresService {
#GET
#Produces("application/json")
Stores getStores();
}
Configuration.java
#ApplicationScoped
public class Configuration {
#ConfigProperty(name = "apiKey.stores")
private String storesApiKey;
public String getKey() {
return storesApiKey;
}
public static String getStoresApiKey() {
return CDI.current().select(Configuration.class).get().getKey();
}
}
StoresController.java which is the REST controller
#Path("/stores")
public class StoresController {
#Inject
#RestClient
StoresService storesService;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Stores getStores() {
return storesService.getStores();
}
}
Late to the party, but putting this here for my own reference. There seems to be a difference in using #ClientHeaderParam and #HeaderParam, so I investigated a little further:
According to the Microprofile docs, you can put a compute method for the value in curly braces. This method can extract the property value.
See link for more examples.
EDIT: What I came up with resembles the original, but uses a default method on the interface, so you can at least discard the Configuration class. Also, using the org.eclipse.microprofile.config.Config and ConfigProvider classes to get the config value:
#RegisterRestClient
#ClientHeaderParam(name = "Authorization", value = "{getAuthorizationHeader}")
public interface StoresService {
default String getAuthorizationHeader(){
final Config config = ConfigProvider.getConfig();
return config.getValue("apiKey.stores", String.class);
}
#GET
#Produces("application/json")
Stores getStores();
I will get rid of the Configuration class and use an #HeaderParam to pass your configuration property from your rest endpoint to your rest client. The annotation will then send this property as an HTTP header to the remote service.
Somthing like this should works:
#Path("/stores")
#RegisterRestClient
public interface StoresService {
#GET
#Produces("application/json")
Stores getStores(#HeaderParam("ApiKey") storesApiKey);
}
#Path("/stores")
public class StoresController {
#ConfigProperty(name = "apiKey.stores")
private String storesApiKey;
#Inject
#RestClient
StoresService storesService;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Stores getStores() {
return storesService.getStores(storesApiKey);
}
}
Say I have an annotation (#RequiresAccount) introduced in another library and I'm using it in my project, is there a way to conditionally apply it to a method, e.g. apply it when the customer is from website A and not apply when customer is from website B?
I've taken a look and the only possibility I've found was, creating a wrapper-Annotation:
#Aspect
#Component
public class RequiresAccountWrapperAspect {
#Autowired
private HttpServletRequest request;
private RequiresAccountAspect requiresAccountAspect = new RequiresAccountAspect();
#Around("#annotation(com.example.demo.components.RequiresAccountWrapper)")
public Object checkIfRequiresAccount(ProceedingJoinPoint joinPoint) throws Throwable {
String requestURL = request.getRequestURL().toString();
if (requestURL.startsWith("http://localhost")) {
requiresAccountAspect.checkAccount(joinPoint);
}
return joinPoint.proceed();
}
}
So everywhere you've used your RequiresAccount annotation, you can use this wrapper instead. For example:
#GetMapping("/test")
#RequiresAccountWrapper
public String h() {
return "test";
}
As you can see I'm creating a new instance of the aspect. I don't know if you have access to the Aspect-class itself but if you have you can then call the method in it and pass the joinPoint. To find the URL from the request you can inject the HttpServletRequest.
I am learning spring boot, and i developed the below simple example. I would like to annotate a class as Controller using #Controller. this class has constructor and I want to have access to GreetingFromDeuController as shown:
http://localhost:8080:/GreetingFromDeuController?str = "hi"
the error i am receiving is
#RequestMapping is not applicable on a constructor
please let me know how to solve.
code:
#Controller
#RequestMapping("/GreetingFromDeuController")
public class GreetingFromDeuController {
private String str;
#RequestMapping("/GreetingFrom/deu")
GreetingFromDeuController(#RequestParam(value = "str") String str) {
this.str = str;
}
#RequestMapping("/GreetingFromDeuController")
public String getGreetingFromDeu() {
return this.str;
}
}
First of all your constructor gets initialize much before you hit your URL. So you need to work on your design or tell me your business requirement and I will try to provide you a solution. My refactor code solution will help you to achieve that in two steps. First hit POST method which will do work on setting variable and then subsequent hits of GET method will return that set value.
We can refactor code like below. It will explain use of RequestMapping on method and class.
Considering we have to write two API, one for reading and one for writing.
URLS :
1. POST http://localhost:8080/example/greetings (in request body send {str:'hi'})
2. GET http://localhost:8080/example/greetings
#Controller
#RequestMapping("/example")
public class GreetingFromDeuController {
private String str;
#RequestMapping(value="/greetings" , method = RequestMethod.POST)
public void setGreetingFromDeu(#RequestBody(value = "str") String str)
{
this.str = str;
}
#RequestMapping(value="/greetings" , method = RequestMethod.GET)
public String getGreetingFromDeu()
{
return this.str;
}
}
The #RequestMapping documentation says:
Annotation for mapping web requests onto methods in request-handling
classes with flexible method signatures.
Then you can not do that, if you want to initialize your variables or whatever you can use several ways:
1.- Use #PostConstruct
#PostContruct
public void init() {
this.str = "Anything";
}
2.- Use a simple request to set anything only
#RequestMapping(value="/refresh/anythings", method = RequestMethod.PUT)
public void refresh(#RequestBody(value = "str") String str) {
this.str = str;
}
3.- Use #Value
In application.properties / application.yaml
properties.str = anything
In the Controller
#Value("${properties.str:default}") // by default str is "default"
public String str;
#RequestMapping(value="/greetings" , method = RequestMethod.GET)
public String getGreetingFromDeu() {
return this.str;
}
As far I am concerned, #RequestMapping is not meant for constructors. It should be used for annotating methods or classes. Methods that are responsible for handling requests.
#RequestMapping should be used to map request with endPoint. which can be used as class level and method level.
You can use #RestController (improved from #Controller see difference).
The ideal flow for Spring Boot is Controller -> Service -> Repository
Controller -> maps request with endPoint and return response
Service -> perform business logic
Repository -> Handle database operation
Example
#RestController
#RequestMapping("/api")
public class GreetingController {
#Autowired GreetinService greetingService;
// Request http://localhost:8080/api/GreetingFrom
#GetMapping("/GreetingFrom")
public ResponseEntity<String> GreetingRequestParam(#RequestParam(value = "name") String name) {
greetingService.performBusinessLogic(name);
return new ResponseEntity<String>("Greetings from "+name,HttpStatus.OK);
}
// Request http://localhost:8080/api/GreetingFrom/user2121
#GetMapping("/GreetingFrom/{name}")
public ResponseEntity<String> GreetingPathVariable(#PathVariable(value = "name") String name) {
return new ResponseEntity<String>("Greetings from "+name,HttpStatus.OK);
}
}
In my endpoint, I have some methods with #GET and some methods with #POST. #GETs are working fine, but #POSTs always return 404.
Here is some part from the endpoint's interface:
public interface TestEndpoint {
#GET
#Path("/ping")
Response ping();
#POST
#Path("/weather/{iata}/{pointType}")
Response updateWeather(#PathParam("iata") String iataCode,
#PathParam("pointType") String pointType,
String datapointJson);
#POST
#Path("/airport/{iata}/{lat}/{long}")
Response addAirport(#PathParam("iata") String iata,
#PathParam("lat") String latString,
#PathParam("long") String longString);
#GET
#Path("/exit")
Response exit();
}
Here is the server initialization part:
public class TestServer {
private static final String BASE_URL = "http://localhost:9090/";
public static void main(String[] args) {
try {
final ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(TestEndpointImpl.class);
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URL), resourceConfig, false);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
server.shutdownNow();
}));
HttpServerProbe probe = new HttpServerProbe.Adapter() {
public void onRequestReceiveEvent(HttpServerFilter filter, Connection connection, Request request) {
System.out.println(request.getRequestURI());
}
};
server.getServerConfiguration().getMonitoringConfig().getWebServerConfig().addProbes(probe);
server.start();
Thread.currentThread().join();
server.shutdown();
} catch (IOException | InterruptedException ex) {
Logger.getLogger(TestServer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
where, TestEndpointImpl is an implementation of TestEndpoint (as the name implies) with class-level annotation #Path("/collect").
When I perform GET requests, it works fine. But POSTs are problematic. Corresponding methods are not called.
As a side note, probe prints both GET and POST requests as expected, so I am sure that requests reach the server and paths are ok.
Is there any suggestion?
EDIT: Some snippet from the implementation:
#Path("/collect")
public class TestEndpointImpl implements TestEndpoint {
...
#Override
public Response updateWeather(#PathParam("iata") String iataCode, #PathParam("pointType") String pointType,
String datapointJson) {
System.out.println("TRACE: " + datapointJson);
// do something and return a Response
}
...
}
The registered probe prints /collect/weather/BOS/wind, but updateWeather is not called.
Short answer
Copy the #POST and the #Path annotations to the method implementation. It will do the trick.
Long answer
The section regarding annotation inheritance of the JAX-RS 2.0 specification (the specification which Jersey is the reference implementation) is pretty clear. See the quote below:
3.6 Annotation Inheritance
JAX-RS annotations may be used on the methods and method parameters of a super-class or an implemented interface. Such annotations are inherited by a corresponding sub-class or implementation class method provided that the method and its parameters do not have any JAX-RS annotations of their own. Annotations on a super-class take precedence over those on an implemented interface. The precedence over conflicting annotations defined in multiple implemented interfaces is implementation specific. Note that inheritance of class or interface annotations is not supported.
If a subclass or implementation method has any JAX-RS annotations then all of the annotations on the superclass or interface method are ignored. E.g.:
public interface ReadOnlyAtomFeed {
#GET
#Produces("application/atom+xml")
Feed getFeed();
}
#Path("feed")
public class ActivityLog implements ReadOnlyAtomFeed {
public Feed getFeed() {...}
}
In the above, ActivityLog.getFeed inherits the #GET and #Produces annotations from the interface. Conversely:
#Path("feed")
public class ActivityLog implements ReadOnlyAtomFeed {
#Produces("application/atom+xml")
public Feed getFeed() {...}
}
In the above, the #GET annotation on ReadOnlyAtomFeed.getFeed is not inherited by ActivityLog.getFeed and it would require its own request method designator since it redefines the #Produces annotation.
For consistency with other Java EE specifications, it is recommended to always repeat annotations instead of relying on annotation inheritance.
That can also happen if the url is not in the correct format; for example you could have sent a request without the correct path parameters.
Is it possible to have Jersey take a series of HTTP headers and marshall them into a POJO, much as one might do with POST parameters?
If you are using jersey 1.x then you can use #InjectParam,
But I would suggest you to upgrade to 2.x and start using #BeanParam
If you want to get access to a specific #HeaderParam as a String, use the answers provided by #Juned Ahsan or #DJ Spiess. If you want to inject them into a POJO, I would recommend using Jersey's #BeanParam in 2.x.
For example:
#Path("/foo")
public class FooResource {
#GET
#Path("/bar")
public void bar(#BeanParam MyBean myBean) {
// Do something
}
}
public class MyBean {
private String uaCompatible;
public MyBean(#HeaderParam("X-UA-Compatible") String uaCompatible) {
this.uaCompatible = uaCompatible;
}
public String getUacompatible() {
return this.uaCompatible;
}
}
#BeanParam can be replaced with #InjectParam for Jersey 1.x (>=1.4) or #Inject in 1.x earlier than 1.4. javax-#Inject can also be used if you're using a dependency injection framework such as Spring.
Yes, you'd do it like this. (from http://www.mkyong.com/webservices/jax-rs/get-http-header-in-jax-rs/)
#Path("/users")
public class UserService {
#GET
#Path("/get")
public Response addUser(#HeaderParam("user-agent") String userAgent) {
return Response.status(200)
.entity("addUser is called, userAgent : " + userAgent)
.build();
}
}
Yes you can do so using the #HeaderParams