How to define several ports under single endpoint in CXF? - soap

we're migrating our SOAP application from AXIS2 to CXF and we have following service definition in the wsdl:
<wsdl:service name="MyService">
<wsdl:documentation>My Service
</wsdl:documentation>
<wsdl:port name="MyServiceHttpSoap11Endpoint" binding="ns:MyServiceSoap11Binding">
<soap:address location="http://localhost:8080/axis2/services/MyService" />
</wsdl:port>
<wsdl:port name="MyServiceHttpSoap12Endpoint" binding="ns:MyServiceSoap12Binding">
<soap12:address location="http://localhost:8080/axis2/services/MyService" />
</wsdl:port>
<wsdl:port name="MyServiceHttpEndpoint" binding="ns:MyServiceHttpBinding">
<wsdl:documentation>documentation ***</wsdl:documentation>
<http:address location="http://localhost:8080/axis2/services/MyService" />
</wsdl:port>
</wsdl:service>
In old AXIS2 implementation I was able to access wsdl at http://hostname:port/MyService/services/MyService?wsdl
which had 3 different endpoints for each SOAP version:
/MyService/services/MyService.MyServiceHttpSoap11Endpoint/
/MyService/services/MyService.MyServiceHttpSoap12Endpoint/
/MyService/services/MyService.MyServiceHttpEndpoint/
But how could I achieve same locations with CXF (without Spring)?
I want to have single wsdl url and 3 different locations for each SOAP version.
I know that SOAP 1.2 is backward compatible in CXF, so it's enough to define just 1.2 endpoint to cover SOAP 1.1 and 1.2. But how to use different names for them? And how could I define 3rd port (MyService.MyServiceHttpEndpoint)?
For non-Spring CXF implementation I extended CXFNonSpringServlet class to publish my service:
public class SimpleCXFNonSpringServlet extends CXFNonSpringServlet {
private static final long serialVersionUID = 1L;
#Override
public void loadBus(ServletConfig servletConfig) {
super.loadBus(servletConfig);
Bus bus = getBus();
BusFactory.setDefaultBus(bus);
Endpoint endpoint = Endpoint.create(new EndpointFactory().createMyServiceEndpoint());
endpoint.publish("/MyService");
}
}
Endpoint factory class:
public class EndpointFactory {
public MyServiceHttpSoap12Endpoint createMyServiceEndpoint() {
return new MyServiceHttpSoap12Endpoint(new MyServiceImpl());
}
}
implementor class:
#WebService(
serviceName = "MyService",
portName = "MyServiceHttpSoap12Endpoint",
targetNamespace = "http://company.com",
wsdlLocation = "file:/C:/projects/MyService_CXF/MyService/WebContent/WEB-INF/services/MyService/META-INF/MyService.wsdl",
endpointInterface = "com.company.MyServicePortType")
public class MyServiceHttpSoap12Endpoint implements MyServicePortType {
private static final Logger LOG = Logger.getLogger(MyServiceHttpSoap12Endpoint.class.getName());
private MyServiceAPI myServiceAPI;
public MyServiceHttpSoap12Endpoint(MyServiceAPI myServiceAPI) {
this.myServiceAPI = myServiceAPI;
}
#Override
public ResultObject getObject(Parms pams) throws MyService_Exception {
LOG.info("Executing operation getObject");
// ... implementation ...
}
}
Web.xml:
<servlet>
<servlet-name>CXFServlet</servlet-name>
<display-name>CXF Servlet</display-name>
<servlet-class>
com.company.SimpleCXFNonSpringServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>

Related

Wildfly with Basic Auth blocks requests with HTTP POST is not allowed for this method

I have a strange problem which is not reproducible at the moment.
I have the following endpoints:
#Path("/v1/")
#Produces(MediaType.APPLICATION_JSON)
public class EndpointVersion1Base
{
private BackendRestClient restClient;
#EJB
public void setRestClient(BackendRestClient restClient)
{
this.restClient = restClient;
}
#Path("/dataprivacy/")
public Object getDataPrivacy()
{
return new DataPrivacyEndpoint(restClient);
}
#Path("/crashreporting/")
public Object getCrashReport()
{
return new CrashReportEndpoint(restClient);
}
}
The endpoint crashreporting has a Basic authentication. The endpoint dataprivacy has no authentication. The dataprivacy endpoint looks like this:
#Path("/")
#Produces(MediaType.APPLICATION_JSON)
public class DataPrivacyEndpoint
{
private BackendRestClient restClient;
private Logger logger = LoggerFactory.getLogger(getClass());
public DataPrivacyEndpoint(BackendRestClient restClient)
{
this.restClient = restClient;
}
public DataPrivacyEndpoint()
{
}
#POST
#Path("/")
#Consumes(MediaType.APPLICATION_JSON)
public Response storeConsent(
#NotNull(message = ErrorCodes.ERR_QUERY_PARAM_NULL) #Valid String consentInputBo) throws ForbiddenException, BadRequestException
{
//some code
}
}
I achieved the Basic Auth of the crashreporting endpoint by the following web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<display-name>publicapi</display-name>
<security-constraint>
<web-resource-collection>
<web-resource-name>Crash reporting</web-resource-name>
<description>crash reporting service</description>
<url-pattern>/v1/crashreporting/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>publicapi</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>UserRoles simple realm</realm-name>
</login-config>
<security-role>
<role-name>publicapi</role-name>
</security-role>
</web-app>
and jboss-web.xml
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web xmlns="http://www.jboss.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-web_5_1.xsd">
<context-root>publicapi</context-root>
<security-domain>other</security-domain>
</jboss-web>
Yesterday all this worked. Today I started the services. Suddenly, as I sent a POST request to the dataprivacy endpoint via http://192.168.0.80:8080/publicapi/v1/dataprivacy/ and I got an HTTP error response "HTTP POST is not allowed for this method".
I wondered why this happened because it worked yesterday. After I restarted the services it suddenly worked again?!. What is going on here? Why does it sometimes work and sometimes not? (Currently I can't reproduce it). Do I have some misconfiguration in here which could lead to some strange behaviour? I'm afraid that this could happen on my LIVE system as well.

JBoss AS 5.1.0 GA resteasy application - force login on every request

I'm using Resteasy 3.0.11.Final with JBoss AS 5.1.0 GA. I have a defined REST web service. The whole service is secured with BASIC authentication with a custom security domain. When I use Postman to send a request (#1) with BASIC authentication for user A, JBoss AS invokes a login module for the user, and then calls a local ejb (looked up with initial context) method with caller principal A. Immidiately after I send another request (#2) with BASIC authentication for user B, in this case JBoss AS does not invoke a login module and calls a local ejb method with caller principal A again. After some time sending a request with user B yields the desired result (local ejb method call with caller principal B). I'm not sure what causes the problem, the Resteasy service configuration / session handling or the JBoss AS security domain configuration which is responsible for login modules (subject timeout? lack of logout after method being called?)? Basically I want to configure Resteasy to force a new session with a new login module invocation for the local ejb method call for every rest request.
web.xml:
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>my-app</display-name>
<context-param>
<param-name>resteasy.providers</param-name>
<param-value>org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider,com.mycompany.infrastructure.ExceptionMapper</param-value>
</context-param>
<context-param>
<param-name>resteasy.resources</param-name>
<param-value>com.mycompany.resource.Resource</param-value>
</context-param>
<listener>
<listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<servlet>
<servlet-name>my-app-resteasy-servlet</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
<init-param>
<param-name>javax.ws.rs.core.Application</param-name>
<param-value>com.mycompany.application.Application</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>my-app-resteasy-servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>my-app-resteasy-servlet</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>User</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>MyRealm</realm-name>
</login-config>
<security-role>
<role-name>User</role-name>
</security-role>
</web-app>
jboss-web.xml
<jboss-web>
<context-root>/path</context-root>
<security-domain>java:/jaas/MyRealm</security-domain>
</jboss-web>
beans.xml
<beans
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
</beans>
login-config.xml for MyRealm
<application-policy name="MyRealm">
<authentication>
<login-module code="com.mycompany.security.UsernamePasswordLoginModuleImpl"
flag="required">
<module-option name="password-stacking">useFirstPass</module-option>
</login-module>
</authentication>
</application-policy>
Resource.java
#Path("/resource")
#Stateless
public class Resource {
#POST
#Path("/execute")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public ResponseDTO execute(RequestDTO dto) {
try {
// code
} catch (Exception exception) {
// handle
}
}
}
I found a very rough solution:
Resource.java:
#Path("/resource")
#Stateless
public class Resource {
#POST
#Path("/execute")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public ResponseDTO execute(RequestDTO dto, #Context HttpServletRequest request) {
try {
// code
} catch (Exception exception) {
// handle
} finally {
if (request != null) {
request.getSession().invalidate();
}
}
}
}
or the same result, different implementation (without repeating the same code in every method in a resource, obviously):
SessionInvalidatorFilter.java
public class SessionInvalidatorFilter implements ContainerResponseFilter {
#Context
private HttpServletRequest request;
public void filter(ContainerRequestContext requestCtx, ContainerResponseContext responseCtx) throws IOException {
if ((request != null) && (request.getSession() != null)) {
request.getSession().invalidate();
}
}
}
web.xml
<context-param>
<param-name>resteasy.providers</param-name>
<param-value>com.mycompany.infrastructure.filter.SessionInvalidatorFilter</param-value>
</context-param>

Spring Resful WS issue

I'm following http://www.java2blog.com/2015/09/spring-restful-web-services-json-example.html and as it's suggested in the article when I deploy my application to tomcat I get the warning:
WARNING: [SetContextPropertiesRule]{Context} Setting property 'source' to 'org.eclipse.jst.j2ee.server:restordering' did not find a matching property.
(restordering is my application context)
And when I try to access my application I get this warning:
WARNING: No mapping found for HTTP request with URI [/restordering] in DispatcherServlet with name 'restordering'
and I see a 404 error page!
I've tested this on tomcat 7 and 8. I've created the war and deployed outside of eclipse and followed all suggested solution including change in server definition (publish module context to separate XML files) and removing and adding and doing all the tricks it's described in the above article and on stackoverflow but nothing has worked so far.
Before I get "this is a warning and ..." yes it is a warning but also I get 404 error and can't access my services.
And yes I have the correct package name in my restordering-servlet.xml ( context:component-scan base-package="com.obp.restordering.controller" ).
As requested here is my web.xml:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>restordering</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>restordering</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
and restordering-servlet.xml is:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<mvc:annotation-driven />
<context:component-scan base-package="com.obp.restordering.controller" />
</beans>
and my controller file is this:
package com.obp.restordering.controller;
import java.util.ArrayList;
import java.util.List;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.obp.restordering.bean.Country;
#RestController
public class CountryController {
#RequestMapping(value = "/countries", method = RequestMethod.GET, headers = "Accept=application/json")
public List<Country> getCountries() {
List<Country> listOfCountries = new ArrayList<Country>();
listOfCountries = createCountryList();
return listOfCountries;
}
#RequestMapping(value = "/country/{id}", method = RequestMethod.GET, headers = "Accept=application/json")
public Country getCountryById(#PathVariable int id) {
List<Country> listOfCountries = new ArrayList<Country>();
listOfCountries = createCountryList();
for (Country country : listOfCountries) {
if (country.getId() == id)
return country;
}
return null;
}
// Utiliy method to create country list.
public List<Country> createCountryList() {
Country indiaCountry = new Country(1, "India");
Country chinaCountry = new Country(4, "China");
Country nepalCountry = new Country(3, "Nepal");
Country bhutanCountry = new Country(2, "Bhutan");
List<Country> listOfCountries = new ArrayList<Country>();
listOfCountries.add(indiaCountry);
listOfCountries.add(chinaCountry);
listOfCountries.add(nepalCountry);
listOfCountries.add(bhutanCountry);
return listOfCountries;
}
}
Any idea?
This is embarrassing but I need to come clean and admit and provide an answer so if others have the same issue they don't circle around days as I did.
Arpit the author of the mentioned tutorial responded to my request for help and after checking my code confirmed all works as expected!
The issue was in how I was trying to access my service! If I go to /restordering/countries I get the 404 error. To access the service I need to go to /restordering/restordering/countries! As simple as that.
The warnings I still get and I can't resolve them at this time but at least the service is working and this seems more of a deployment/setting issue than actual spring development issue.
Anyways, I thought this may help some others.
Cheers

Spring rest call not working

I had written a very small rest service. When I am trying through the rest cilent, I am getting the error as "method not supported". Can anybody, please suggest me on this.
**controller class**
#Controller
#RequestMapping("/movie")
public class MovieController {
#RequestMapping(value="/{name}", method = RequestMethod.PUT, consumes="application/json")
public #ResponseBody Student getMovie(#PathVariable String name, ModelMap model, #RequestBody Student student, HttpSession session) {
Map<Integer, Student> empData = new HashMap<Integer, Student>();
empData.put(1, student);
return student;
}
}
**Request I am sending throught Rest DHC Client**
URL: http://localhost:8081/SpringMVC/movie/test
method selected: PUT
Headers: Content-Type:application/json
Body: {
"userId":"21",
"firstName":"srinu",
"lastName":"nivas"
}
I use only Jersey to do what you want.
My web.xml file is:
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name>appName</display-name>
<filter>
<filter-name>jersey</filter-name>
<filter-class>com.sun.jersey.spi.container.servlet.ServletContainer</filter-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.appName.rest</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.property.JSPTemplatesBasePath</param-name>
<param-value>/Pages/</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.property.WebPageContentRegex</param-name>
<param-value>/(resources|(Pages))/.*</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>jersey</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
And in the rest class i return a Viewable:
My rest class:
#GET
#Path("/PathToAccessPage")
#Produces(MediaType.TEXT_HTML)
public Viewable GetMyClassPage(#Context HttpServletRequest request, #Context UriInfo ui, #Context HttpHeaders hh) {
try {
MyClass myClass = getMyClass();
return new Viewable("/page", myClass);
}
catch (Exception ex) {
return new Viewable("/error", "Error processing request: " + ex.getMessage());
}
}
Hope you help!
Finally, I got the solution. I didn't import the below two jars.
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
As I am using spring MVC and as you can see in the above code, I am giving as consumes=application/json, I thought springMVC will take care of that. It's my mistake and also, I think the error which was renedered by spring, should be something specific to this. Anyway, got the solution. Thanks all for the help.

Cannot make REST call with url-pattern other than /*

I'm trying to do a simple REST Web Application using Tomcat 7, Apache Wink, and the Jackson JSON Processor, but seem to be hitting a wall. If I look in my web.xml file, I see:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Example Web Application</display-name>
<servlet>
<servlet-name>ExampleServlet</servlet-name>
<servlet-class>org.apache.wink.server.internal.servlet.RestServlet</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.dummy.example.server.ExampleApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ExampleServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
</web-app>
Now, if I substitute /* for the URL pattern instead, the REST call works, but when I use /services/*, it fails.
In my ExampleApplication I see:
package com.dummy.example.server;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;
import org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider;
import org.codehaus.jackson.map.AnnotationIntrospector;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector;
import org.codehaus.jackson.xc.JaxbAnnotationIntrospector;
public class ExampleApplication extends Application {
/**
* Get the list of service classes provided by this JAX-RS application
*/
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> serviceClasses = new HashSet<Class<?>>();
serviceClasses.add(com.dummy.example.server.services.Employee.class);
return serviceClasses;
}
#SuppressWarnings("deprecation")
#Override
public Set<Object> getSingletons() {
Set<Object> s = new HashSet<Object>();
// Register the Jackson provider for JSON
// Make (de)serializer use a subset of JAXB and (afterwards) Jackson annotations
// See http://wiki.fasterxml.com/JacksonJAXBAnnotations for more information
ObjectMapper mapper = new ObjectMapper();
AnnotationIntrospector primary = new JaxbAnnotationIntrospector();
AnnotationIntrospector secondary = new JacksonAnnotationIntrospector();
AnnotationIntrospector pair = new AnnotationIntrospector.Pair(primary, secondary);
mapper.getDeserializationConfig().setAnnotationIntrospector(pair);
mapper.getSerializationConfig().setAnnotationIntrospector(pair);
// Set up the provider
JacksonJaxbJsonProvider jaxbProvider = new JacksonJaxbJsonProvider();
jaxbProvider.setMapper(mapper);
s.add(jaxbProvider);
return s;
}
}
And in my Employee class, I have:
package com.dummy.example.server.services;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.json.simple.JSONObject;
#Path("/services/employee")
#Produces(MediaType.APPLICATION_JSON)
#SuppressWarnings("unchecked")
public class Employee {
#GET
public JSONObject get() {
JSONObject json = new JSONObject();
json.put("Name", "Example");
return json;
}
}
Any ideas? I've been banging my head against this for some time now
The url-pattern parameter for your servlet (in the web.xml) is independent from the path you specify in your Employee class.
<servlet-mapping>
<servlet-name>ExampleServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
means your servlet listens on the /services/ sub-path.
#Path("/services/employee")
means that your REST application listens on the /services/employee "sub-sub-path".
So your webservice is exposed at localhost:8080/example/services/services/employee (the concatenation of the url-pattern and the #Path annotation).
If you want to expose it at localhost:8080/example/services/employee with the mentioned url-pattern, you need to change the Employee class needs to have:
#Path("employee")
Where would you expect /services/* to go? The web-app states what URL patterns the web application is willing to service. This makes the application server (e.g., JBoss, GlassFish) route /services/foo/bar/whatever to your web application. The Employee class will be invoked in response to a request to /services/employee so you can make that request. Nothing else is registered so /services/* should result in either a 404 or 400 response. Since /services/* is registered to your web application, I would expect a 400.