When I start camel in standalone mode I get a message that my routes are consuming from Endpoints that I have set up:
Route: route1 started and consuming from: Endpoint[http://localhost:9090/hrm/hrm_push?bindingStyle=SimpleConsumer]
Great!
But when I cut & past whats in between [] into my browser I'm getting a 404.
Surely if Camel says it is consuming at that address I should be able to use that address to contact my Rest web service.
Here is my appContext
<bean id="transformer" class="com.xxxx.portlistener.services.Transformer">
</bean>
<cxf:rsServer id="pushServer"
address="http://localhost:9090/hrm/hrm_push?bindingStyle=SimpleConsumer" >
<cxf:serviceBeans>
<ref bean="transformer" />
</cxf:serviceBeans>
</cxf:rsServer>
<cxf:rsServer id="pingServer"
address="http://localhost:9090/hrm/hrm_ping" >
<cxf:serviceBeans>
<ref bean="transformer" />
</cxf:serviceBeans>
</cxf:rsServer>
<!-- Camel Configuration -->
<camel:camelContext id="camel-1" xmlns="http://camel.apache.org/schema/spring">
<package>com.xxxx.portlistener.services</package>
<camel:route id="route1">
<camel:from uri="cxfrs://bean://pushServer"/>
<camel:to uri="log:TEST?showAll=true" />
</camel:route>
<camel:route id="route2">
<camel:from uri="cxfrs://bean://pingServer"/>
<camel:to uri="log:TEST?showAll=true" />
</camel:route>
</camel:camelContext>
My Service Interface:
#Path("/hrm/")
public interface PushService
{
/**
* trasform will change the given Object....
*/
#POST
#Produces("text/plain")
#Path("/hrm_push/")
public Response pusher(Object pushee);
#GET
#Produces("text/plain")
#Path("/hrm_ping/")
public Response ping();
}
The error from the console:
Jan 21, 2014 10:45:50 AM org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor processRequest
WARNING: No root resource matching request path / has been found.
Jan 21, 2014 10:45:51 AM org.apache.cxf.jaxrs.impl.WebApplicationExceptionMapper toResponse
WARNING: WebApplicationException has been caught : no cause is available
Can anyone spot what I'm doing wrong?
Thanks,
Andrew
You have duplicated path settings in the CXF RS bean and in the Java annotations. The two will be combined, therefore the final URL will be something like http://localhost:9090/hrm/hrm_push + "/hrm/" + "/hrm_push/" which is probably not what you wanted.
A recommendation would be to use the CXF RS bean to define the base URL only, then use the Java annotations for everything else.
Related
I'm invoking a web service that requires WS-Addressing SOAP headers. I'm using Apache Camel with CXF to invoke the web service. When I configure the CXF endpoint with the web service's WSDL, it's smart enough to automatically add WS-Adressing SOAP headers, but I need to set a custom MessageId.
Here is the message that is currently being sent:
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Header>
<ws:international xmlns:ws="http://www.w3.org/2005/09/ws-i18n">
<ws:locale xmlns:ws="http://www.w3.org/2005/09/ws-i18n">en_CA</ws:locale>
</ws:international>
<fram:user wsa:IsReferenceParameter="true" xmlns:fram="http://wsbo.webservice.ephs.pdc.ibm.com/Framework/" xmlns:wsa="http://www.w3.org/2005/08/addressing">BESTSystem</fram:user>
<Action soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">http://webservice.ephs.pdc.ibm.com/Client/QueryHumanSubjects</Action>
<MessageID soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">urn:uuid:945cfd10-9fd2-48f9-80b4-ac1b9f3293c6</MessageID>
<To soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">https://panweb5.panorama.gov.bc.ca:8081/ClientWebServicesWeb/ClientProvider</To>
<ReplyTo soap:mustUnderstand="true" xmlns="http://www.w3.org/2005/08/addressing">
<Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
</ReplyTo>
</soap:Header>
<soap:Body>
<ns2:queryHumanSubjectsRequest xmlns:ns2="http://wsbo.webservice.ephs.pdc.ibm.com/Client/" xmlns:ns3="http://wsbo.webservice.ephs.pdc.ibm.com/FamilyHealth/">
<!-- stuff -->
</ns2:queryHumanSubjectsRequest>
</soap:Body>
</soap:Envelope>
As you can see, the MessageId value is "urn:uuid:945cfd10-9fd2-48f9-80b4-ac1b9f3293c6". I need to set a custom value.
I tried adding the MessageId header they way I add the other headers like "international" and "user", but some part of the framework overrides the value.
// Note this doesn't work! Something overrides the value. It works for other headers.
#Override
public void process(Exchange exchange) throws Exception {
Message in = exchange.getIn();
List<SoapHeader> headers = CastUtils.cast((List<?>) in.getHeader(Header.HEADER_LIST));
SOAPFactory sf = SOAPFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
QName MESSAGE_ID_HEADER = new QName("http://www.w3.org/2005/08/addressing", "MessageID", "wsa");
SOAPElement messageId = sf.createElement(MESSAGE_ID_HEADER);
messageId.setTextContent("customValue");
SoapHeader soapHeader = new SoapHeader(MESSAGE_ID_HEADER, messageId);
headers.add(soapHeader);
}
The CXF website has some documentation on how to set WS-Addressing headers, but I don't see how to apply it to Apache Camel. The Apache Camel CXF documentation doesn't specifically mention WS-Addressing either.
The documentation links you posted actually do have the information you need, although it's not immediately obvious how to apply it to Camel.
The CXF documentation says that:
The CXF org.apache.cxf.ws.addressing.impl.AddressingPropertiesImpl object can be used to control many aspects of WS-Addressing including the Reply-To:
AddressingProperties maps = new AddressingPropertiesImpl();
EndpointReferenceType ref = new EndpointReferenceType();
AttributedURIType add = new AttributedURIType();
add.setValue("http://localhost:9090/decoupled_endpoint");
ref.setAddress(add);
maps.setReplyTo(ref);
maps.setFaultTo(ref);
((BindingProvider)port).getRequestContext()
.put("javax.xml.ws.addressing.context", maps);
Note that it sets the addressing properties on the "RequestContext".
The Apache Camel documentation says that:
How to propagate a camel-cxf endpoint’s request and response context
CXF client API provides a way to invoke the operation with request and response context. If you are using a camel-cxf endpoint producer to invoke the outside web service, you can set the request context and get response context with the following code:
CxfExchange exchange = (CxfExchange)template.send(getJaxwsEndpointUri(), new Processor() {
public void process(final Exchange exchange) {
final List<String> params = new ArrayList<String>();
params.add(TEST_MESSAGE);
// Set the request context to the inMessage
Map<String, Object> requestContext = new HashMap<String, Object>();
requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, JAXWS_SERVER_ADDRESS);
exchange.getIn().setBody(params);
exchange.getIn().setHeader(Client.REQUEST_CONTEXT , requestContext);
exchange.getIn().setHeader(CxfConstants.OPERATION_NAME, GREET_ME_OPERATION);
}
});
The above example has some stuff we don't need, but the important thing is that it shows us how to set the CXF Request Context.
Put them together and you get:
#Override
public void process(Exchange exchange) throws Exception {
AttributedURIType messageIDAttr = new AttributedURIType();
messageIDAttr.setValue("customValue");
AddressingProperties maps = new AddressingProperties();
maps.setMessageID(messageIDAttr);
Map<String, Object> requestContext = new HashMap<>();
requestContext.put(JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES, maps);
exchange.getIn().setHeader(Client.REQUEST_CONTEXT, requestContext);
}
// org.apache.cxf.ws.addressing.JAXWSAConstants.CLIENT_ADDRESSING_PROPERTIES = "javax.xml.ws.addressing.context"
// org.apache.cxf.endpoint.Client.REQUEST_CONTEXT = "RequestContext"
Warning: In my route, I invoke multiple different web services sequentially. I discovered that after setting the RequestContext as shown above, Camel started using the same RequestContext for all web services, which resulted in an error: "A header representing a Message Addressing Property is not valid and the message cannot be processed". This is because the incorrect "Action" header was used for all web service invocations after the first.
I traced this back to Apache Camel using a "RequestContext" Exchange property, separate from the header we set, which apparently takes priority over the header. If I remove this property prior to calling subsequent web services, CXF automatically fills in the correct Action header.
if your problem not solved, I suggest you to combine your cxf service with custom interceptor. it easy to work with your soap message. like this:
<bean id="TAXWSS4JOutInterceptorBean" name="TAXWSS4JOutInterceptorBean" class="com.javainuse.beans.SetDetailAnswerInterceptor " />
<cxf:cxfEndpoint id="CXFTest" address="/javainuse/learn"
endpointName="a:SOATestEndpoint" serviceName="a:SOATestEndpointService"
serviceClass="com.javainuse.SOATestEndpoint"
xmlns:a ="http://javainuse.com">
<cxf:binding>
<soap:soapBinding mtomEnabled="false" version="1.2" />
</cxf:binding>
<cxf:features>
<wsa:addressing xmlns:wsa="http://cxf.apache.org/ws/addressing"/>
</cxf:features>
<cxf:inInterceptors>
<ref bean="TAXWSS4JInInterceptorBean" />
</cxf:inInterceptors>
<cxf:inFaultInterceptors>
</cxf:inFaultInterceptors>
<cxf:outInterceptors>
<ref bean="TAXWSS4JOutInterceptorBean" />
</cxf:outInterceptors>
<cxf:outFaultInterceptors>
</cxf:outFaultInterceptors>
</cxf:cxfEndpoint>
and in the interceptor you can set soap headers like this:
public class SetDetailAnswerInterceptor extends WSS4JOutInterceptor {
public SetDetailAnswerInterceptor() {
}
#Override
public void handleMessage(SoapMessage mc) {
AttributedURIType value = new AttributedURIType();
value.setValue("test");
((AddressingProperties) mc.get("javax.xml.ws.addressing.context.outbound")).setMessageID(value);
}
}
I am using Spring 4, with Spring Security 4 to secure web-services. A normal web-service worked fine without security. My secured web-services also seemed to work great working locally and with unit testing.
We are using the SiteMinder example, which means we have authentication happenind from a remote authority. This delivers a token to the browser when we login. We pass the authentication token with a request header, and this is pulled from customerUserDetailsService. That class and methods pull the token from the header, authenticates the user against that remote authority, and we get a username. From that username, we make a DAO call to our database to get the User Details and their Roles, which in the Spring Security Context uses the roles to grant authority. This is all working fine, and we get an authenticated user and we have thier roles/grantedAuthorities.
So, as previously stated, we are now just securing web-services with spring-security.xml to secure the web-services based on the role of the user. Again, this all seemed to work fine with unit testing. We had a token for a user who did not have access to web-sites and we correctly got back a 403 error. When we used a token for a user who did have the right role, was able to execute the web-service.
Now I am trying to deploy this into a new environment, and I am not having much luck.
So, I have a spring-security.xml that looks like:
<http use-expressions="true" auto-config="false" entry-point-ref="http403EntryPoint">
<!-- Additional http configuration omitted -->
<intercept-url pattern="/records/authorizedRecords" access="hasRole('portalUser')" />
<intercept-url pattern="/records/myCode" access="hasRole('portalUser')" />
<intercept-url pattern="/users/email" access="hasRole('appAdmin')" />
<custom-filter position="PRE_AUTH_FILTER" ref="openAmFilter" />
</http>
<beans:bean id="openAmFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<beans:property name="principalRequestHeader" value="openam_token"/>
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
<beans:bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<beans:property name="preAuthenticatedUserDetailsService">
<beans:bean id="userDetailsServiceWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<beans:property name="userDetailsService" ref="customUserDetailsService"/>
</beans:bean>
</beans:property>
</beans:bean>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="preauthAuthProvider" />
</authentication-manager>
<beans:bean id="customUserDetailsService" class="com.agmednet.server.security.CustomUserDetailsService"></beans:bean>
<beans:bean id="http403EntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint">
When we tried access to:
<intercept-url pattern="/users/email/*" access="hasRole('appAdmin')" />
this did not seem to match:
/rest/users/email/myemail#someemail.com
so I changed it to:
<intercept-url pattern="/rest/users/email/*" access="hasRole('appAdmin')" />
And I get this from the log.
DEBUG: org.springframework.security.web.util.matcher.AntPathRequestMatcher - Checking match of request : '/rest/trials/integratedtrials'; against '/rest/users/email/*'
DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Public object - authentication not attempted
DEBUG: org.springframework.security.web.FilterChainProxy - /rest/trials/integratedTrials reached end of additional filter chain; proceeding with original chain
DEBUG: org.springframework.security.web.access.ExceptionTranslationFilter - Chain processed normally
DEBUG: org.springframework.security.web.context.SecurityContextPersistenceFilter - SecurityContextHolder now cleared, as request processing completed
At this point, I know we have an authenticated user, the username, and the roles are listed right there in the logs. We seem to find a matching URL in the spring-security.xml, the URL matches, the roles match, and now I feel we should be executing the logic behind the web-service, but I get an error message:
Apache Tomcat/8.0.30 - Error report
/services/rest/users/email/myemail#someemail.com
The requested resource is not available.
I am absolutely floored here .... unsecured web-services work great. I have to be missing something? Is it with the word "rest" in the URL? Web-services have always worked when they were unsecured. I added security and add unit tests to test this all out, and now I am unsure of what happened?
The problem is that I am an idiot!!!!
In the /WEB-INF/web.xml file I had this defined:
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
I suppose that is why when I call any RESTful web-services with:
/rest/users/email/myemail#someemail.com
it doesn't work.
But when I use:
/api/users/email/myemail#someemail.com
now it works.
Conclusion: I am an idiot!!!
I have the WSDL file for the SOAP webservice that i need to invoke over http. Using cxf wsdl2java plugin i have created the stub methods.
I have created the webservice client using jaxws. The webservice has basic authentication enabled. I am trying to configure http conduit
my application.properties
--------------------------
webservices.http.auth.username=username
webservices.http.auth.password=password
fold.webservices.http.auth.authtype=Basic
webservices.http.conduit.property.name=https://fixed_deposits-test.co.in/fold-webservices/services.*
fold.updateservice.soap.address=https://fixed_deposits-test.co.in/fold-webservices/services/UpdateService
----------------------------
My Spring Context...
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:http-conf="http://cxf.apache.org/transports/http/configuration"
xmlns:sec="http://cxf.apache.org/configuration/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd
http://cxf.apache.org/configuration/security http://cxf.apache.org/schemas/configuration/security.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<bean id="properties" class="org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer">
<property name="locations">
<util:list>
<value>file:${config.dir}/application.properties</value>
</util:list>
</property>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
</bean>
<jaxws:client id="updateServiceClient" serviceClass="com.fold.facade.v1.UpdateService" address="${fold.updateservice.soap.address}" >
<jaxws:inInterceptors>
<bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor" >
<property name="prettyLogging" value="true" />
</bean>
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor" >
<property name="prettyLogging" value="true" />
</bean>
</jaxws:outInterceptors>
</jaxws:client>
<http-conf:conduit name="***?????????***">
<http-conf:authorization>
<sec:UserName>${fold.webservices.http.auth.username}</sec:UserName>
<sec:Password>${fold.webservices.http.auth.password}</sec:Password>
<sec:AuthorizationType>${fold.webservices.http.auth.authtype}</sec:AuthorizationType>
</http-conf:authorization>
</http-conf:conduit>
I have done a lot of searching online so as to what should be the valid value for name attribute..
accouring to CXF documentation it should be...
{WSDL_endpoint_target_namespace}PortName.http-conduit
my WSDL File has..
...
targetNamespace="http://facade.fold.com/" and
...
<wsdl:port binding="tns:UpdateServiceImplServiceSoapBinding"
name="UpdateServiceImplPort">
<soap:address
location="https://fixed_deposits-test.co.in/fold-webservices/services/UpdateService" />
</wsdl:port>
so i tried with these..
<http-conf:conduit name="{http://facade.fold.com/}UpdateServiceImplPort.http_conduit">
or
<http-conf:conduit name="*UpdateServiceImplPort.http_conduit">
or
<http-conf:conduit name="{http://facade.fold.com/}*.http_conduit">
But none of them work as i get 401 unauthorized exception..
org.apache.cxf.transport.http.HTTPException: HTTP response '401: Unauthorized' when communicating with https://fixed_deposits-test.co.in/fold-webservices/services/UpdateService
THERE ARE COUPLE OF WAYS I GOT IT TO WORK
a) <http-conf:conduit name="*.http_conduit">
but i really don't want to do it this way...
b) <http-conf:conduit name="https://fixed_deposits-test.co.in/fold-webservices/services/UpdateService">
this is hardcoding the SOAP service URL... which i don't want as i am looking for externalizing URL as my SOAP URL's are different for different environment..(dev /test /prod etc)
Below is my best shot at externalization, but it failed with 401 Unauthorized Exception...
properties were replaced in all other instances in my spring context, but not for http-conf:conduit name attribute :(
<http-conf:conduit name="${webservices.http.conduit.property.name}">
As a workaround i am currently using the regex approach which works..
<http-conf:conduit name="*.*/fold-webservices/services/UpdateService">
But i really want to figure out if it possible to externalize it and read from properties file. And i want to do it the Spring
configuration way. Please help me !!!
We had the same issue with JBoss Fuse 6.1 (Camel CXF 2.12.0).
You can see what the http-conf:conduit name is set to by enabling DEBUG log level and looking at your log, there should be a log line such as:
DEBUG 10:40:41,437 [Camel (cnpms-as-mnp-ctx) thread #0 - JmsConsumer[cnpms-queue]] org.apache.cxf.transport.http.HTTPConduit.setTlsClientParameters(HTTPConduit.java:901) - Conduit '{http://acme.com/manageporting/v1}ManageportingPortPort.http-conduit' has been (re)configured for plain http.
So in this case you would set the name as:
<http-conf:conduit name="{http://acme.com/manageporting/v1}ManageportingPortPort.http-conduit"
But the Web Service (WS) Interface Class is defined as:
#WebService(targetNamespace = "http://acme.com/manageporting/v1", name = "ManageportingPort")
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface ManageportingPort {
Generated from WSDL:
<wsdl:portType name="ManageportingPort">
Note that by following the CXF documentation you would expect the port name component to be "ManageportingPort" NOT "ManageportingPortPort" i.e. with "Port" appended to it.
However looking at how the portQName is resolved in org.apache.cxf.jaxws.support.JaxWsImplementorInfo.getEndpointName(), if the port name is not set in the WS Interface Class, and the name is not null or blank, it sets the port name to portName = name + "Port" otherwise it sets it to portName = implementorClass.getSimpleName() + "Port".
I had the same problem...
In my case the following 2 changes helped:
1) add "Port" postfix to the port name, despite it is not defined in the wsdl this was
e.g wsdl:
<wsdl:port name="XXXSoap">
will be "XXXSoapPort" in the conduit definition
2) remove the "\" at the end of the target namespace name
==> therefore try
<http-conf:conduit name="{http://facade.fold.com}UpdateServiceImplPort.http_conduit">
or
<http-conf:conduit name="{http://facade.fold.com}UpdateServiceImplPortPort.http_conduit">
I came across the same challenge and found no existing solution. Spring doesn't seem to resolve placeholders in bean names (make lots of sense). However, this is a valid case unless cxf allows conduit matching using another attribute.
There are a few ways to solve this problem:
Define conduit beans programmatically (lose the neat of xml declaration)
Find a way to resolve the bean names which contains placeholders
I prefer option 1 and this is the implementation which I'm happy with. Please note that PropertyPlaceholderResolver is our own utility which uses the same defined resolver bean.
public class NameWithPlaceholderBeanFactoryPostProcessor implements BeanFactoryPostProcessor
{
#Override
public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) throws BeansException
{
if (!(beanFactory instanceof DefaultListableBeanFactory))
return;
DefaultListableBeanFactory bf = (DefaultListableBeanFactory) beanFactory;
String[] names = bf.getBeanDefinitionNames();
for (String name : names)
{
if (name.indexOf('$') < 0)
continue;
BeanDefinition bd = bf.getBeanDefinition(name);
bf.removeBeanDefinition(name);
bf.registerBeanDefinition(PropertyPlaceholderResolver.resolvePlaceHolders(name), bd);
}
}
}
The final step is to define this as a spring bean.
I have developed a webservice following the below link, however I am unable to get the request parameters from the POST request body.
http://info.appdirect.com/blog/how-to-easily-build-rest-web-services-with-java-spring-and-apache-cxf
I use the Soap UI to invoke the
service, with "Post QueryString in Message Body" option checked.
So far, I have tried below options, but none seem to work:
Tried MultiValued Map, but the map is always empty.
#POST
#Path("/getpostfile/{fileName}")
#Produces("application/pdf")
#Consumes("application/x-www-form-urlencoded")
public Response getPostFile(MultivaluedMap form){...}
Tried #FormParam() & #QueryParam as well, but still the params are null in webservice method.
Tried creating a POJO bean with #XmlRootElement annotation, however this time I get an exception saying "SEVERE: No message body reader has been found for class com.wcc.LoginInfo, ContentType: application/x-www-form-urlencoded". LoginInfo is my bean class with two parameters:
#XmlRootElement
public class LoginInfo {
String username;
String password;
....
Below is the service method:
#POST
#Path("/getpostfile/{fileName}")
#Produces("application/pdf")
#Consumes("application/x-www-form-urlencoded")
public Response getPostFile(LoginInfo logininfo){...}
Below is my cxf-beans.xml file:
<bean .....>
<jaxrs:server id="wccservice" address="/">
<jaxrs:serviceBeans>
<ref bean="wccdocumentservice" />
</jaxrs:serviceBeans>
<jaxrs:providers>
<ref bean="formUrlEncodeProvider" />
</jaxrs:providers>
</jaxrs:server>
<bean id="wccdocumentservice" class="com.wcc.WCCDocumentServiceImpl" />
<bean id="formUrlEncodeProvider" class="org.apache.cxf.jaxrs.provider.FormEncodingProvider" />
<bean id="jsonProvider" class="org.apache.cxf.jaxrs.provider.json.JSONProvider">
<property name="marshallAsJaxbElement" value="true" />
</bean>
Any ideas on how to retrieve the POST parameters in request body, in the Webservice method?
Guess I was dwelling too much into CXF, missed the simple approach. We can get the Post request parameters in CXF service similar to the way we get the parameters in a Servlet.
For Query Parameters in POST request:
#POST
#Path("/getpostfile/{fileName}")
#Produces("application/pdf")
#Consumes("application/x-www-form-urlencoded")
public Response getPostFile(#PathParam("fileName") String fileName, #Context HttpServletRequest request)
{
System.out.println("id is : " + request.getParameter("id")+", password is : " + request.getParameter("password"));
and for Parameters in POST message body, you may use getBody() method from below link:
Getting request payload from POST request in Java servlet
Hope that helps someone!
After I got my single-page web app working (web pages served with ServiceStack's RazorFormat() MVC, not .ASP MVC), I ran a (previously passing) test for the service. The test failed. Tested the web app again (debug run, navigate to //localhost:1337/ResourceList in the browser): still working. Is something wrong with my test?
Here's the error:
Test Name: TestResourceList
Test FullName: [0-1015]ServiceWrapper.Test.TestSWrapperServices.TestResourceList
Test Source: c:\Users\uname\Documents\Visual Studio 2012\Projects\ServiceWrapper\UnitTestProject1\ServiceTests.cs : line 96
Test Outcome: Failed
Test Duration: 0:00:02.188
Result Message:
System.Net.WebException : Unable to connect to the remote server
----> System.Net.Sockets.SocketException : No connection could be made because the target machine actively refused it 127.0.0.1:1337
Result StackTrace:
at System.Net.HttpWebRequest.GetResponse()
at ServiceStack.ServiceClient.Web.ServiceClientBase.Send[TResponse](String httpMethod, String relativeOrAbsoluteUrl, Object request)
at ServiceStack.ServiceClient.Web.ServiceClientBase.Get[TResponse](IReturn`1 request)
at ServiceWrapper.Test.TestSWrapperServices.TestResourceList() in c:\Users\uname\Documents\Visual Studio 2012\Projects\ServiceWrapper\UnitTestProject1\ServiceTests.cs:line 98
--SocketException
at System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddress socketAddress)
at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Socket s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state, IAsyncResult asyncResult, Exception& exception)
Here's the test:
namespace ServiceWrapper.Test
{
[TestFixture]
public class TestSWrapperServices
{
AppHost appHost;
private const string ListeningOn = "http://*:1337/";
public const string Host = "http://localhost:1337";
private const string BaseUri = Host + "/";
[TestFixtureSetUp]
public void OnTestFixtureSetUp()
{
var appSettings = new AppSettings();
var username = Environment.GetEnvironmentVariable("USERNAME");
var userdomain = Environment.GetEnvironmentVariable("USERDOMAIN");
AppHost.AppConfig = new AppConfig(new AppSettings());
appHost = new AppHost();
// initialize Service Server
ServiceServer.SetUser(AppHost.AppConfig.UserName, AppHost.AppConfig.Password);
ServiceServer.SetLog(String.Empty);
try
{
appHost.Init();
appHost.Start(ListeningOn);
}
catch (HttpListenerException ex)
{
if (ex.ErrorCode == 5)
{
System.Diagnostics.Debug.WriteLine("You need to run the following command (as admin):");
System.Diagnostics.Debug.WriteLine(" netsh http add urlacl url={0} user={1}\\{2} listen=yes",
ListeningOn, userdomain, username);
}
else
{
System.Diagnostics.Debug.WriteLine("ERROR: {0}: {1}", ex.GetType().Name, ex.Message);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("ERROR: {0}: {1}", ex.GetType().Name, ex.Message);
}
}
[TestFixtureTearDown]
public void OnTestFixtureTearDown()
{
appHost.Dispose();
}
[Test]
public void TestResourceList()
{
JsonServiceClient client = new JsonServiceClient(BaseUri);
ResourceList response = client.Get(new ResourceList());
Assert.Contains("Some Value", response.property);
}
[Test]
}
}
I upgraded to the latest ServiceStack - 3.9.55, and it still didn't work. So, I started over again, sanity checking from the beginning. It turns out that the program.cs ListeningOn has http://*:1337/ while the nunit TestFixture ListeningOn was http://localhost:1337/
Checking urlacl (as admin) for http://localhost:1337/:
C:\Windows\system32>netsh http show urlacl url=http://localhost:1337/
URL Reservations:
-----------------
Checking urlacl (as admin) for http://*:1337/:
C:\Windows\system32>netsh http show urlacl url=http://*:1337/
URL Reservations:
-----------------
Reserved URL : http://*:1337/
User: DOMAIN\user
Listen: Yes
Delegate: No
SDDL: D:(A;;GX;;;S-1-5-21-2595267603-2801715271-1705165942-1002)
My earlier troubleshooting left the two projects with inconsistent ListeningOn values. Interestingly, using http://*:1337/ doesn't work as a wildcard url, as perhaps I had expected.
Here's a handy code snippet to help you build the add urlacl command. It also provides a useful (!) sanity check on the exact url you're listening on.
Console.WriteLine("You need to run the following command:");
Console.WriteLine(" netsh http add urlacl url={0} user={1}\\{2} listen=yes",
ListeningOn, userdomain, username);
--- Update ---
Upgrading ServiceStack eliminated the 'connection actively refused' error message. Once ListeningOn values were unified, the real
error message was exposed:
Result Message: ServiceStack.ServiceClient.Web.WebServiceException : Service Unavailable
Result StackTrace:
at ServiceStack.ServiceClient.Web.ServiceClientBase.ThrowWebServiceException[TResponse](Exception ex, String requestUri)
at ServiceStack.ServiceClient.Web.ServiceClientBase.ThrowResponseTypeException[TResponse](Object request, Exception ex, String requestUri)
at ServiceStack.ServiceClient.Web.ServiceClientBase.HandleResponseException[TResponse](Exception ex, Object request, String requestUri, Func`1 createWebRequest, Func`2 getResponse, TResponse& response)
at ServiceStack.ServiceClient.Web.ServiceClientBase.Send[TResponse](String httpMethod, String relativeOrAbsoluteUrl, Object request)
at ServiceStack.ServiceClient.Web.ServiceClientBase.Get[TResponse](IReturn`1 request)
at RemoteServerWrapper.Test.TestRSWrapperServices.TestDataList() in c:\Users\user\Documents\Visual Studio 2012\Projects\RemoteServerWrapper\UnitTestProject1\ServiceTests.cs:line 183
It's still obscure -- but at least it's not reporting something that's completely different from the real issue. So then I implemented trace in my app.config, like this:
<configuration>
<!-- ... other config settings ... -->
<system.diagnostics>
<sources>
<source name="System.Net" tracemode="includehex" maxdatasize="1024">
<listeners>
<add name="System.Net"/>
<add name="console"/>
</listeners>
</source>
<source name="System.Net.HttpListener">
<listeners>
<add name="System.Net"/>
<add name="console"/>
</listeners>
</source>
</sources>
<switches>
<add name="System.Net" value="Verbose"/>
<add name="System.Net.HttpListener" value="Verbose"/>
</switches>
<sharedListeners>
<add name="console"
type="System.Diagnostics.ConsoleTraceListener"
initializeData="false"/>
<add name="System.Net"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="network.log"
/>
</sharedListeners>
<trace autoflush="true"/>
</system.diagnostics>
</configuration>
Which exposed a better error message:
ERROR: [::1]:1337 Request not found: /datarequest?DataKey=some_key&startDate=20130701&endDate=20130708
OK - now I have to pull in the servicestack sources so I can step through the code and figure out why I'm getting 'Not Found' in the test, when it works when I 'debug/run' and test via the browser. Turns out that RestHandler.FindMatchingRestPath(httpMethod, pathInfo, contentType) wasn't returning a match. Humm. Why is that? The AppHost is declared identically. So, what's different?
The rest services live in my project's main assembly. When run from 'debug/run' the default assembly has the services, and everything works. But when run from the test project, with the services assembly added as a reference, servicestack can't find them. They're not in the default location, relative to the test project. So I added an AppHost class at the top of my test file, rather than relying on the one from my program.cs, and declared it as follows:
public class RSWrapperServicesAppHostHttpListener
: AppHostHttpListenerBase
{
public RSWrapperServicesAppHostHttpListener()
: base("RSWrapper Services Tests", typeof(DataRequestService).Assembly) { }
// 'DataRequestService' is a random rest service class,
// defined in the referenced services assembly
}
Now ServiceStack is happy, and my tests work again.
How did they ever work? Originally everything was jumbled together all in one project. Once I separated things into separate assemblies, i.e. DTO, Services, Business Logic and Tests, I broke it. But since I was temporarily holding off on unit tests while getting the UI working, I didn't notice right away.