I have a few SOAP webservices I need to proxy with a REST frontend. REST API operations would map 1-1 to their SOAP equivalents.
For instance for invoking operation operation1 for the SOAP webservice at http://soapservices/ServiceA its REST proxy would be POST http://restservices/ServiceA/operation1, with the same exact data binding objects for arguments and return value.
I'll use Camel to dynamically route REST invocations to their corresponding SOAP endpoints, and possibly perform some common pre or post processing.
Ideally I'd like to have a project where I just add the proxied WSDL's, have the JAX-WS Service Endpoint Interfaces generated with Maven's cxf-codegen-plugin, and dynamically instance CxfEndpoint beans for the services using a properties file that will enumerate them.
For the JAX-RS part, I've found reusing the generated SEI's to be quite convenient, and have used a SpringJAXRSServerFactoryBean that gets its resourceClasses set programmatically at application startup, reading enumerated services from the same properties file.
I've got a working draft project, but having to manually change the generated SEI's to add JAX-RS annotations (#Path, #Consumes, #Produces, #Post and so on) looks just bad.
For instance:
For this, and N in general generated SEI's like this one:
#WebService(...)
#XmlSeeAlso(...)
#SoapBinding(...)
public interface ServiceAPortType {
#WebMethod(operationName="operation1")
#WebResult(...)
public ResponseObject operation1(#WebParam(...) ParamObject param);
}
I have a yml file:
services:
- service: ServiceA
config:
address: http://soapservices/ServiceA
serviceClass: ServiceAPortType.class
- service: ServiceB
config:
address: http://soapservices/ServiceB
serviceClass: ServiceBPortType.class
Then register CxfEndpoints for the clients:
#PostConstruct
public void registerSOAPClients(){
Map<String, Object> values = (Map<String, Object>) ws.getConfig(); // "ws" injected with #ConfigurationProperties
BeanDefinitionBuilder bdb = BeanDefinitionBuilder.rootBeanDefinition(CxfEndpoint.class);
for (Map.Entry<String, Object> val : values.entrySet()) {
bdb.addPropertyValue(val.getKey(), val.getValue());
}
beanFactory.registerBeanDefinition(ws.getService(), bdb.getBeanDefinition());
}
The JAX-RS part is the one that needs manual tweaking of the generated SEI's to add #Path, #Consumes and various annotations to let SpringJAXRSServerFactoryBean use them:
#Bean
public SpringJAXRSServerFactoryBean jaxRSfactoryBean() throws ClassNotFoundException {
SpringJAXRSServerFactoryBean bean = new SpringJAXRSServerFactoryBean();
bean.setAddress("/restservices");
bean.setResourceClasses(jaxRSAnnotatedSEIs); // jaxRSAnnotatedSEIs injected from yml serviceClass
return bean;
}
The Camel route is pretty simple and along the lines of:
from("cxfrs:bean:jaxRSfactoryBean?providers=#jsonProvider")
.setHeader("serviceName", getServiceFromURI())
.setHeader("operationName", getOperationFromURI())
.toD("cxf:bean:${header.serviceName}Service?")
.transform().simple("${body.get(0)}")
.marshal().json(JsonLibrary.Jackson)
.end();
Is there any way I could automate adding these annotations so generated source stays unedited, or generating JAX-RS SEI's from a SOAP WSDL or the existing JAX-WS SEIs? Or maybe there is a different cleaner approach? I'm open to alternative approaches as long as they don't involve manual modifications of generated sources and rely on properties files only for enumerating and configuring services.
Related
I'm learning about Apache Camel routes in Spring Boot projects and I have a project that does extension from some endpoints. The endpoints are not in this project, only the extension is done here. The extension is done using #Consume from org.apache.camel in this way:
#Consume(uri = "direct:products.create.validate.interceptor")
public void executeCreate(RequestWrapper<Product> productWrapper) {
...
}
I try to understand how this direct:products.create.validate.interceptor is mapped to an endpoint from another service. Can somebody explain me how this #Consume annotation does the mapping?
Or another example is this:
#Consume(uri = "direct:listAccountsPostHook")
public void executeCreate(RequestWrapper<Account> accountWrapper) {
...
}
Where should I look to understand how they are mapped? In the controller of the other service? I can't find any example with #Consume. Thank you!
The #Consume annotation in Apache Camel is used to subscribe to a Camel endpoint and consume messages from it. The endpoint can be either a direct endpoint or any other type of endpoint such as a JMS queue or a REST endpoint, depending on your use case.
The endpoint URI, which is specified in the uri attribute of the #Consume annotation, determines where the messages are consumed from. In your example, direct:products.create.validate.interceptor and direct:listAccountsPostHook are both direct endpoints.
In Apache Camel, direct endpoints are in-memory endpoints that allow you to send messages directly to another endpoint in the same JVM. The mapping between the endpoint and the method that consumes the messages is done by Camel's routing engine.
More on Camel Direct endpoints you can read here.
To understand how the messages are being consumed, you should look at the Camel routes that are defined in your project. In a Spring Boot project, you can define your Camel routes in a RouteBuilder class. This is where you would specify the mapping between the direct endpoint and the method that will consume the messages.
For example, if you have a RouteBuilder class that looks like this:
public class MyRouteBuilder extends RouteBuilder {
#Override
public void configure() {
from("direct:products.create.validate.interceptor")
.to("bean:myBean?method=executeCreate");
}
}
In this example, the direct endpoint direct:products.create.validate.interceptor is mapped to the executeCreate method in the myBean bean. The ?method=executeCreate part of the to URI tells Camel to call the executeCreate method on the myBean bean when a message is received at the endpoint.
So, in short, you should look for the Camel routes in your project that define the mapping between the endpoint and the method that consumes the messages.
I'm recently working with microservices, developed as Spring Boot applications (v 2.2) and in my company we're using Keycloak as authorization server.
We chose it because we need complex policies, roles and groups, and we also need the User Managed Authorization (UMA) to share resources between users.
We configured Keycloak with a single realm and many clients (one client per microservice).
Now, I understand that I need to explicitly define Resources within Keycloak and this is fine, but the question is: do I really need to duplicate all of them in my microservice's property file?
All the documentation, examples and tutorials end up with the same thing, that is something like:
keycloak.policy-enforcer-config.enforcement-mode=PERMISSIVE
keycloak.policy-enforcer-config.paths[0].name=Car Resource
keycloak.policy-enforcer-config.paths[0].path=/cars/create
keycloak.policy-enforcer-config.paths[0].scopes[0]=car:create
keycloak.policy-enforcer-config.paths[1].path=/cars/{id}
keycloak.policy-enforcer-config.paths[1].methods[0].method=GET
keycloak.policy-enforcer-config.paths[1].methods[0].scopes[0]=car:view-detail
keycloak.policy-enforcer-config.paths[1].methods[1].method=DELETE
keycloak.policy-enforcer-config.paths[1].methods[1].scopes[0]=car:delete
(this second example fits better our case because it also uses different authorization scopes per http method).
In real life each microservice we're developing has dozens of endpoints and define them one by one seems to me a waste of time and a weakness in the code's robustness: we change an endpoint, we need to reconfigure it in both Keycloak and the application properties.
Is there a way to use some kind of annotation at Controller level? Something like the following pseudo-code:
#RestController
#RequestMapping("/foo")
public class MyController {
#GetMapping
#KeycloakPolicy(scope = "foo:view")
public ResponseEntity<String> foo() {
...
}
#PostMapping
#KeycloakPolicy(scope = "bar:create")
public ResponseEntity<String> bar() {
...
}
}
In the end, I developed my own project that provides auto-configuration capabilities to a spring-boot project that needs to work as a resource server.
The project is released under MIT2 license and it's available on my github:
keycloak-resource-autoconf
I am working on porting an old HTTP server to Micronaut and I am stuck trying to port an authorization filter that used the javax.ws.rs NameBinding annotation to a Micronaut HTTP server filter. 90% of my endpoints/controllers use the NameBinding annotation I have so using the standard Micronaut HTTP server filter would be difficult.
One code smelly thought was to create a filter accepting all api endpoints (ie. #Filter("/**")) and then maybe storing a list of all the paths that don't require authorization and comparing that against the requested path.
Another hack I attempted to was to try and derive the target method with reflections through the request/chain but it seems that target method is held in an #Internal class which leads me to believe I should not be reflecting on the method from a filter. If I was able to reflect on the target method from a filter I could look for my old annotation and filter on that.
In general are there any guiding principles for providing filters to a large subset of controllers/methods excluding a handful, for example an inverse filter pattern (although this would also not be ideal)?
Is there any way in micronaut to manually control the injection of filters?
If you need a fine grained control over your endpoints, I'll go for micronaut AOP
#Documented
#Retention(RUNTIME)
#Target(ElementType.METHOD)
#Around
#Type(AuthenticatedInterceptor.class)
public #interface Authenticated {
}
and the interceptor coresponding
#Singleton
public class AuthenticatedInterceptor implements MethodInterceptor<Object, Object> {
#Override
public Object intercept(MethodInvocationContext<Object, Object> context) {
final var authHeader = ServerRequestContext.currentRequest()
.map(HttpMessage::getHeaders)
.flatMap(HttpHeaders::getAuthorization)
.orElseThrow(() -> new RuntimeException("no header"));
validate(authHeader);
return context.proceed();
}
}
then you'll have to add #Authenticated on each methods that need to be authenticated.
UPDATE
Micronaut security provides it's own #Secured annotation.
I need to manage different versions that come in the URL of the Webservice. I'm using a common method in the Webservice for SOAP and REST, and when I tried to get the path in the endPoint class I got the correct path for REST, but not for SOAP(I got a null in this case), does anybody how to get the path in soap?
The url looks like: http://localhost:8083/webService/v1/test and the code is:
#Stateless
#WebService(endpointInterface = "ItestgEndpoint", serviceName="testService")
#Component("testEndpoint")
#Path("/webService")
#Consumes({MediaType.APPLICATION_XML})
#Produces({MediaType.APPLICATION_XML})
public class TestEndpoint implements ItestgEndpoint{
#PUT
#Path("/{version}/test")
#Consumes({MediaType.APPLICATION_XML })
#Produces({MediaType.APPLICATION_XML })
#WebResult(name="testResponse")
public testResponse testEvent(#WebParam(targetNamespace="http://test/web", name="message")
#RequestParam MessageClass message,
#WebParam(name="version") #PathParam("version") String version
) throws TimeoutException, EMSException, ValidationException, AuthenticationException {
logger.info(version);
}
I saw this post SOAP and REST Webservice with one implementation in Java EE, but it didn't solve my problem.
Thanks
SOAP has no "path" like REST has. Depending on the service toolkit you use (CXF, axis, ...) you may need to collect the information in the request that you can extract from the transport mechanism of the soap message. This may or may not be available to you.
Usually, you wire the SOAP service to a path in a configuration. For example in axis, you set the soap servlet dispatcher in the web.xml, same is for cxf. The actual service is then wired in the beans.xml or services.wsdd.
The SOAP handler finds the service by the name of the endpoint and will then send the call to that endpoint which will dispatch it to the right method. The method is in the transmitted SOAP header, not in the URI.
In REST, the identification of the target service/method is in the URI, not in the XML.
So, in my opinion, for SOAP, this is by declaration and the wiring is static, while in REST, you can have the version as a path parameter.
UPDATE: Since everything is possible if you just try hard enough :-) you could rewrite the dispatcher code to allow an extra path parameter on SOAP, maybe the available packages allow some kind of interceptor class, that allows you to rewrite the SOAP header to point to another endpoint, depending on a header attribute that you invent.
We've build an application using Spring and deployed it with Tomcat. We have a working REST interface, however one of our clients only has a SOAP client.
My understanding is that a SOAP web service and a REST web service cannot coexist on the same port or application.
What are my options for accepting a SOAP request with as little development as possible. Should I accept a soap packet via the rest interface and parse the XML? Or can I setup a SOAP interface communicate with my REST interface and respond back?
I'm using Gradle as my build tool. It would be nice to have the solution as part of a single WAR file
In my experience, you can mix SOAP and REST in the same application if you're very careful about XML namespaces for JAXB. However, I wouldn't recommend it since updating one means risking the other's stability. Here is what I recommend...
Setup a multi-project build in gradle
Create three projects, one for the business logic, one for the REST interface, and one for the SOAP interface
Modify the REST/SOAP interface to use common business logic project
Deploy as two separate WARs
Should I accept a soap packet via the rest interface and parse the XML?
SOAP is a protocol and not just a format so this probably won't work with most (any?) frameworks.
Or can I setup a SOAP interface communicate with my REST interface and respond back?
You probably could at the expense of performance and/or maintainability.
We have a project that has similar requirements. We still have to support SOAP and we're going forward with ReST.
There is no reason that the two will conflict. Since you're using spring, you can even have the same domain objects as a response that gets marshalled to XML and JSON as your preference.
What you have to do is create different URI for the two. e.g someService/** for the SOAP and some-rest for the ReST implementations. You can have a service layer to handle shared logic (mostly the code needed on the end point and the rest controller is to fetch the required data from the service layer and sending it to be marshalled)
Just add some entry to your web.xml file to indicate the rest path and the endpoint paths...
It sounds like your web service is primarily REST (it's 2013) but you have to support soap for a limited case. I'd design your web service with rest primarily in mind, but perhaps use a separate mechanism to indicate to the server that the client requires soap support. If possible, have the soap client send an http request header or use modified URL that perhaps ends in .soap. In any case there's no reason why you can't support both protocols on the same app.
You can do that by following this Steps:
-Add annotation of both Rest and Soap on class implementation.
-Creating interface to hold the method annotation for Soap.
-Put Rest annotation on method in class implementation.
-Configure "web.xml" file to add "servlet" for Rest implementation you use.
-Don't forget to create class extend Application like [ApplicationConfiguration.class].
1- Class Implementation
#javax.jws.WebService(endpointInterface = "com.test.ebpp.autopayment.tess.ejb.GService", targetNamespace = "http://ejb.test.autopayment.ebpp.tess.com/", serviceName = "ApplicationBusinessFacadeService", portName = "ApplicationBusinessFacadePort")
#Path(value = "/ApplicationBusinessFacadeService")
public class ApplicationBusinessFacadePortBindingImpl implements
ApplicationBusinessFacade {
#Override
#POST
#Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public ProcessResponse process(Process request) {
//type your code
}
}
2- Service Interface
#WebService(name = "ApplicationBusinessFacade", targetNamespace = "http://ejb.gateway.ebpp.com/")
#XmlSeeAlso({
com.ebpp.gateway.ejb.ObjectFactory.class,
com.ebpp.ifxmessages.ObjectFactory.class
})
public interface ApplicationBusinessFacade {
#WebMethod
#WebResult(targetNamespace = "")
#RequestWrapper(localName = "process", targetNamespace = "http://ejb.gateway.ebpp.com/", className = "com.ebpp.gateway.ejb.Process")
#ResponseWrapper(localName = "processResponse", targetNamespace = "http://ejb.gateway.ebpp.com/", className = "com.ebpp.gateway.ejb.ProcessResponse")
public ProcessResponse process(
#WebParam(name = "arg0", targetNamespace = "")
Process arg0);
}
3- web.xml
<servlet>
<servlet-name>com.ebpp.core.rs.config.ApplicationConfiguration</servlet-name>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>com.ebpp.core.rs.config.ApplicationConfiguration</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</servlet>