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>
Related
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.
Some time ago in one of the projects I found #WebService annotations on some of the jersey root(#Path) resource classes. As far as I understood at the time it was some legacy code or simply a misused JAX-WS annotation. Recently I stumbled upon this post in which mixing JAX-RS service with #WebService annotation for the sake of EJB mentioned(as a side note, that project I worked on didn't make use of EJB at all, so I still think it was an improper use of #WebService). As a result of all that I am now confused if it is in general justifiable to mix #WebService and JAX-RS. What are the cases for that? Anything apart from EJB features mentioned?
Exposing a JAX-RS bean as the methods of a SOAP WS using #WebService may be technically possible. It will not lead to a good API design.
Consider some very common JAX-RS methods:
#GET
#Path("/foos")
#Produces("application/json")
public Response getFoos() {
// get all Foos
List<Foo> foos = ...;
return Response.ok(foos).build();
}
#GET
#Path("/foos/{id}")
#Produces("application/json")
public Response getSingleFoo(#PathParam("id") String id) {
// get the Foo
Foo foo = ...;
return Response.ok(foo).build();
}
It is immediatley obvious how the URLs to call these methods will be structured and what the result will be.
But exposing these methods using #WebService leads to many questions:
What is a Response in a SOAP response?
Will the response use JSON as the representation?
How are the methods called?
I can imagine no usecase that is not completely trivial for which it makes sense to expose the same method using both JAX-RS and JAX-WS. It can either be a useful method for one but not for both.
Don't do this.
The only web services I've ever integrated with or used have been RESTful. I'm now trying to integrate with a 3rd party SOAP service and am awe-struck at how seemingly convoluted SOAP appears to be.
With REST, I use a JAX-RS client called Jersey that makes hitting RESTful endpoints a piece a' cake. For instance, if a service is exposing a POST endpoint at http://api.example.com/fizz (say, for upserting Fizz objects), then in Jersey I might make a service client that looks like this (pseudo-code):
// Groovy pseudo-code
class Fizz {
int type
boolean derps
String uid
}
class FizzClient {
WebResource webResource = initAt("https://api.example.com")
upsertFizz(Fizz fizz) {
webResource.path("fizz").post(fizz)
}
}
But Java-based SOAP clients seem, at first blush, to be fairly more complicated. If I understand the setup correctly, the general process is this:
Obtain an XML document called a WSDL from the service provider; this appears to be a language-agnostic description of all the available endpoints
Run a JDK tool called wsimport on the WSDL which actually generates Java source code, which implements JAX-WS APIs and actually represents my SOAP client
Import those generated source files into my project and use them
First off, if anything I have said about this process is incorrect, please begin by correcting me! Assuming I'm more or less correct, what I don't understand is: why is this necessary if its all an HTTP conversation? Why couldn't I achieve SOAP-based conversations with Jersey, and bypass all this source-generation boilerplate?
For instance, say the same endpoint exists, but is governed by SOAP:
class FizzClient {
WebResource webResource = initAt("https://api.example.com")
FizzSerializer serializer // I take Fizz instances and turn them into XML
FizzDeserializer deserializer // I take XML and turn them into Fizz instances
upsertFizz(Fizz fizz) {
String xmlFizz = serializer.serialize(fizz)
webResource.path("fizz").post(xmlFizz)
}
}
If I understand SOAP correctly, its just a way of utilizing HTTP verbs and request/response entities to send app-specific messages around; it's an HTTP "conversation". So why couldn't I hijack a REST framework like Jersey to HTTP POST messages, and in doing so, bypass this SOAP overhead?
This is going to attract opinion-based answers, but first, you should understand that
jax-rs is much younger than jax-ws (jax-ws had a final draft in 2006, JAX-RS came out in 2008-9).
RESTful webservices standard, for many purposes is quite amorphous - many businesses prefer the comfort of a contract in the form of a WSDL.
Not to mention that JAX-WS, in concert with WS-I provides many other standards that govern security, message reliability and other enterprise-goodies (under the generic "WS-*" banner) that businesses care about. There's a hodge-podge of libraries that are attempting to get that kind of uniformity on to the jax-rs platform, but for now, jax-ws/WS-I is the industry standard
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.
I have two Java EE applications on two separate application servers. One of them already contains a working EJB. I want to be able to communicate with this EJB from the other application by using a JAX-WS webservice (the communication has to work between different application servers and different server versions, so remote EJB call is no option). It is no problem to expose the server api to the client application.
The server side is quite clear, adding #Webservice annotation seems to work quite well. But i wonder what is the best way to build the client: I don't really want to generate the client stub from the wsdl (which itself has been generated from the ejb code by the container in my case) and pack all these generated classes into the client ear - but this seems to be the only way i can make use of #WebServiceRef annotations.
The alternative to make a dynamic proxy myself with the help of the static methods of javax.xml.ws.Service (sth. like service=Service.create() and service.getPort()) is not recommended by the Spec and "container providers are not required to support managed Service Instances created using these methods".
But that is exactly sth. that I want to use:
Is there a way to get a dynamic proxy injected in my code, managed by the application server? Or is the only way to get a managed webservice client instance to be done with generated client stub classes?
Read JAX-WS 2.2 spec, Chapter 4: Client APIs.
1. Static Client Generation
Is really the simplest way to work with JAX-WS. From a web services perspective, the WSDL is the interface AND the connection properties. Even if you choose not to work with it physically, you still need to know it in a logical sense to make meaningful SOAP calls.
Note from JAX-WS spec: An Endpoint that uses the SOAP 1.1/HTTP binding MUST
make its contract available as a WSDL 1.1 document at the publishing address suffixed with ?WSDL or ?wsdl
2. Dynamic Client Programming
Is there a way to get a dynamic proxy injected in my code, managed by the application server?
This approach involves dynamic programming against the JAX-WS API to connect to a web service either with or without using WSDL. There's no way to just "inject" a dynamic proxy out of nowhere. You need to construct & configure one with the SEI's port URLs. The WSDL document is the standard place to store such configuration information, although it is possible to avoid it and to programmatically insert the info.
2A) Dynamic programming with WSDL:
javax.xml.ws.Service service = Service.create(
new URL("http://example.org/stocks.wsdl"),
new QName("http://example.org/stocks", "StockQuoteService"));
com.example.StockQuoteProvider proxy = service.getPort(portName,
com.example.StockQuoteProvider.class)
javax.xml.ws.BindingProvider bp = (javax.xml.ws.BindingProvider)proxy;
Map<String,Object> context = bp.getRequestContext();
context.setProperty("javax.xml.ws.session.maintain", Boolean.TRUE);
proxy.getLastTradePrice("ACME");
Advantages over (1): can dynamically dynamically change the WSDL doc after app is deployed, provided such changes do not affect the java interface to client.
i.e. very little benefit to you. Your WSDL is static. Whilst you could point your client to <service endpoint URL>?wsdl to dynamically lookup, this means you need to manually configure <service endpoint URL> AND that leaves little else that can change in the SEI/WSDL without impacting your client logic.
2B) Dynamic programming without WSDL:
String endpointUrl = ...;
QName serviceName = new QName("http://example.org/wssample/echo/", "EchoService");
QName portName = new QName("http://example.org/wssample/echo/", "EchoServicePort");
/** Create a service and add at least one port to it. **/
Service service = Service.create(serviceName);
service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING, endpointUrl);
/** Create a Dispatch instance from a service.**/
Dispatch<SOAPMessage> dispatch = service.createDispatch(portName,
SOAPMessage.class, Service.Mode.MESSAGE);
/** Create SOAPMessage request. **/
// compose a request message
MessageFactory mf = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
// Create a message. This example works with the SOAPPART.
SOAPMessage request = mf.createMessage();
SOAPPart part = request.getSOAPPart();
// Obtain the SOAPEnvelope and header and body elements.
SOAPEnvelope env = part.getEnvelope();
SOAPHeader header = env.getHeader();
SOAPBody body = env.getBody();
// Construct the message payload.
SOAPElement operation = body.addChildElement("invoke", "ns1",
"http://com/ibm/was/wssample/echo/");
SOAPElement value = operation.addChildElement("arg0");
value.addTextNode("ping");
request.saveChanges();
/** Invoke the service endpoint. **/
SOAPMessage response = dispatch.invoke(request);
Advantage (not really): can eventually get it to carry out same behaviour as above.
Disadvantages: Complex programming. Non-standard configuration (outside of WSDL). Need to avoid hard-coding settings. Brittle to interface changes. Manually synchronising settings between server and client - easy to omit something, extremely difficult to debug.
Answer:
Go back to (1). Generate a client stub from the WSDL. Use it as an interface contract - it should be designed well and not change.
Then spend the time you save solving real problems... ;) ;)