How to mock a complex REST call from the server side? - web-services

While working with javascript that uses REST services extensively -- including using vocabs like GET, PUT, POST, DELETES, etc; I have found it hard to mock the server side so front end development can go on independently (of back end).
It is also useful to sometimes capture multi-step data, so we can help reproduce the entire chain of REST even (or bugs related to the front end that are triggered from these chains)
What tools can I use to mock REST calls, esp stateful ones? (i.e. if I do a PUT on some resource, I expect the next GET on it to change somehow)
I tried SOAPUI 4.0.1 and it's REST mocking is disappointing. Plus, my need is beyond single state mocking (which anyone can do with a static .json file). I need to do state transition type of mocks; working with Content-Range headers would be best.
Anyone?

I actually ended up creating my own Java REST Mock Engine that can basically mock any response. As long as you can handcraft or cut-paste a text file that simulates the entire http response, you can use my solution to mock the service.
Here's the servlet:
package com.mockrest.debug;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* Servlet implementation class MockGridData
*/
public class MockRest extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* #see HttpServlet#HttpServlet()
*/
public MockRest() {
super();
// TODO Auto-generated constructor stub
}
#Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
sub:{
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
String setdata = request.getParameter("__setdata");
if (setdata!=null && setdata.length()>0){
System.err.println("Setting Data...");
HttpSession sess = request.getSession(true);
String data = "/"+request.getParameter("__setdata");
sess.setAttribute("data", data);
try{
InputStream is = getServletContext().getResourceAsStream(data);
if (is!=null){
is.close();
response.getWriter().write("Successfully pointed next REST call to:"+data);
}
else{
response.sendError(500, "Cannot find resource:"+data);
}
}
catch (IOException ioe){
response.sendError(500, Arrays.deepToString(ioe.getStackTrace()));
}
}
else{
System.err.println("Fetching Data...");
HttpSession sess = request.getSession(false);
if (sess==null || sess.getAttribute("data")==null){
response.sendError(500,"Session invalid or no Previous Data Set!");
}
String rsrc = (String)sess.getAttribute("data");
System.err.println("Resource Being used:"+rsrc);
InputStream is = getServletContext().getResourceAsStream(rsrc);
if (is!=null){
String statusline = readLine(is);
Pattern statusPat = Pattern.compile("^HTTP/1.1 ([0-9]+) (.*)$");
Matcher m = statusPat.matcher(statusline);
if (m!=null && m.matches()){
int status = Integer.valueOf(m.group(1));
response.setStatus(status, m.group(2));
}
else{
throw new ServletException("Bad input file: status line parsing failed, got this as status line:"+statusline);
}
String line;
Pattern httpHeaderPat = Pattern.compile("^([^:]+): (.*)$");
while ((line=readLine(is))!=null){
if (line.length()==0){
// end of headers
break;
}
Matcher m2 = httpHeaderPat.matcher(line);
if (m2!=null && m2.matches()){
response.setHeader(m2.group(1), m2.group(2));
}
}
OutputStream os = response.getOutputStream();
byte[] buf = new byte[1024];
int size;
while ((size=is.read(buf))>0){
os.write(buf, 0, size);
}
os.flush();
}
}
}
}
private String readLine(InputStream is) throws IOException {
StringBuffer sb = new StringBuffer();
char c;
while ((c=(char)is.read())!='\n'){
sb.append(c);
}
if (sb.charAt(sb.length()-1) == '\r'){
sb.deleteCharAt(sb.length()-1);
}
return sb.toString();
}
}
To configure it, place prebuilt response files inside your WebContent folder. I usually end these files with .http extensions.
An example init.http file is below. Pretend we placed this file inside a folder called data inside WebContent:
HTTP/1.1 200 OK
Date: Wed, 26 Oct 2011 18:31:45 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-AspNet-Version: 4.0.30319
Content-Range: items 0-1/2
Content-Length: 385
Cache-Control: private
Content-Type: application/json
[
{
"id": "249F0",
"field1": " Global",
"displaystartdate": "2007-10-20",
"displayenddate": "2012-10-20",
"status": "Major Delay",
"children": true
},
{
"id": "962581",
"field2": "Europe",
"displaystartdate": "2007-10-20",
"displayenddate": "2012-10-20",
"status": "Major Delay",
"children": true
}
]
Headers must separate with body by an empty line (no spaces, nada). People familiar with http will notice it's a pure http response. This is on purpose.
You can use this tool to simulate any of the http headers you want the response to have; even going so far to respond with different server header(in my example, I simulated the response pretending to be IIS 6.0); or a different HTTP status code, etc.
To invoke it from your browser/javascript; first prime it with:
http://yourserver/yourweb/MockGridData?__setdata=data/init.http
Then in your javascript or REST AJAX call, if it goes to
http://yourserver/yourweb/MockGridData
with any method or parameter; it will get the http response you previously crafted with; even down to the Content-Range; Cache headers; etc. If you then need the subsequent AJAX call to return something else, simply call with __setdata again. I suggest you setup a few buttons to do the explicit state transition in your web app.
Assuming everything is setup, for a simulated REST chain, a developer may do:
invoke
http://yourserver/yourweb/MockGridData?__setdata=data/init.http
run a javascript module that will result in calling (say, with GET)
http://yourserver/yourweb/MockGridData
click a button that then does:
http://yourserver/yourweb/MockGridData?__setdata=data/step1.http
run another javascript step that will result in calling (say, with PUT)
http://yourserver/yourweb/MockGridData
click another button that then does:
http://yourserver/yourweb/MockGridData?__setdata=data/step2.http
run another javascript step that will result in calling (say, with GET)
http://yourserver/yourweb/MockGridData
but this time expecting different result than #4.
This should even work with binary and gzipped responses, but I haven't tested that.

Here is another homegrown rest-mocking tool: https://github.com/mkotsur/restito.

Related

how to extract response as json from http ng-builder

The URI myresourceUrl when hit in browser shows the json content in browser.
Requirement:
Need to use the get method of httpbuilder-ng to call GET of URI and response should have the content as json.
This json file will be necessary as input for another task.
How to achieve this. Do we need any parser to get the returned response as json using http-builder-ng.
Expected response format:
{"name":"Abc","info":{"age":45,"height":"5.5"}}
Tried the below get request using:
// setting the request URI
HttpBuilder http = HttpBuilder.configure(config -> {
config.getRequest().setUri(myresourceUrl);
});
String response = http.get(LazyMap.class, cfg -> {
cfg.getRequest().getUri().setPath(myPath);
}).toString();
Actual format we are getting:
{name:Abc,info:{age:45,height:5.5}}
How to get the response indicated above in expected response format.
First, confirm that you http request is indeed returning a JSON response. If so, you can use the gson library. Try
import com.google.code.gson;
String response = gson.toJSON(http.get(LazyMap.class, cfg -> {
cfg.getRequest().getUri().setPath(myPath);
}));
By default a content-type of "application/json" will be parsed rather than returned as a String. You need to define a custom parser for the content-type. I put together an example using a fake server that returns "application/json" content and then shows how to return it as a string in HttpBuilder-NG:
import com.stehno.ersatz.ErsatzServer;
import groovyx.net.http.HttpBuilder;
import static com.stehno.ersatz.ContentType.APPLICATION_JSON;
import static groovyx.net.http.NativeHandlers.Parsers.textToString;
public class Runner {
public static void main(final String[] args) {
final ErsatzServer server = new ErsatzServer();
server.expectations(expects -> {
expects.get("/something").responder(response -> {
response.body("{\"name\":\"Abc\",\"info\":{\"age\":45,\"height\":\"5.5\"}}", APPLICATION_JSON);
});
});
final String response = HttpBuilder.configure(cfg -> {
cfg.getRequest().setUri(server.getHttpUrl());
cfg.getResponse().parser("application/json", (chainedHttpConfig, fromServer) -> textToString(chainedHttpConfig, fromServer));
}).get(String.class, cfg -> {
cfg.getRequest().getUri().setPath("/something");
});
System.out.println(response);
System.exit(0);
}
}
Note the cfg.getResponse().parser("application/json", (chainedHttpConfig, fromServer) -> textToString(chainedHttpConfig, fromServer)); line which is where the parsing happens (overrides the default behavior) - see also the import import static groovyx.net.http.NativeHandlers.Parsers.textToString;.

Cross Origin Filter with embedded Jetty

I'm trying to get a CrossOriginFilter working with a couple of embedded Jetty servers, both running on our internal network. Both are running servlets, but I need server A's web page to be able to post to server B's servlets. I think I need to add ACCESS_CONTROL_ALLOW_ORIGIN to a CrossOriginFilter but finding out how to do this with an embedded Jetty instance with no web.xml isn't proving to be easy. I get the following error message in the browser when trying to access server b's serlvets
No 'Access-Control-Allow-Origin' header is present on the requested resource
Im using angularjs to post to the other server's servlets in a controller.
And here is the code for one of the servers (both are pretty much the same)
Server server = new Server(httpPort);
ResourceHandler resource_handler = new ResourceHandler();
resource_handler.setDirectoriesListed(true);
resource_handler.setWelcomeFiles(new String[] { "index.html" });
resource_handler.setResourceBase("./http/");
ServletHandler handler = new ServletHandler();
handler.addServletWithMapping(ServerPageRoot.class, "/servlet/*");
FilterHolder holder = new FilterHolder(CrossOriginFilter.class);
holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,POST,HEAD");
holder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "X-Requested-With,Content-Type,Accept,Origin");
handler.addFilter(holder );
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { resource_handler, handler,new DefaultHandler() });
server.setHandler(handlers);
server.start();
A few points:
Don't use ServletHandler naked like that. The ServletHandler is an internal class that ServletContextHandler uses.
The ServletContextHandler is what provides the needed ServletContext object and state for the various servlets and filters you are using.
The ServletContextHandler also provides a place for the overall Context Path declaration
The ServletContextHandler is also the place for Welcome Files declaration.
Don't use ResourceHandler, when you have DefaultServlet available, its far more capable and feature rich.
Example:
Server server = new Server(httpPort);
// Setup the context for servlets
ServletContextHandler context = new ServletContextHandler();
// Set the context for all filters and servlets
// Required for the internal servlet & filter ServletContext to be sane
context.setContextPath("/");
// The servlet context is what holds the welcome list
// (not the ResourceHandler or DefaultServlet)
context.setWelcomeFiles(new String[] { "index.html" });
// Add a servlet
context.addServlet(ServerPageRoot.class,"/servlet/*");
// Add the filter, and then use the provided FilterHolder to configure it
FilterHolder cors = context.addFilter(CrossOriginFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST));
cors.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
cors.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
cors.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,POST,HEAD");
cors.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "X-Requested-With,Content-Type,Accept,Origin");
// Use a DefaultServlet to serve static files.
// Alternate Holder technique, prepare then add.
// DefaultServlet should be named 'default'
ServletHolder def = new ServletHolder("default", DefaultServlet.class);
def.setInitParameter("resourceBase","./http/");
def.setInitParameter("dirAllowed","false");
context.addServlet(def,"/");
// Create the server level handler list.
HandlerList handlers = new HandlerList();
// Make sure DefaultHandler is last (for error handling reasons)
handlers.setHandlers(new Handler[] { context, new DefaultHandler() });
server.setHandler(handlers);
server.start();
managed to get it working by doing
FilterHolder holder = new FilterHolder(CrossOriginFilter.class);
holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,POST,HEAD");
holder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "X-Requested-With,Content-Type,Accept,Origin");
holder.setName("cross-origin");
FilterMapping fm = new FilterMapping();
fm.setFilterName("cross-origin");
fm.setPathSpec("*");
handler.addFilter(holder, fm );
Maybe this will help someone even though it is not a good answer to the original question. I realized that you can easaly enable cross origin request sharing in an embedded jetty instance by manipulating the headers directly in your handler. The response object below is an instance of HttpServletResponse (which is passed to the handler).
Example:
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Credentials", "true");
response.addHeader("Access-Control-Allow-Methods", "POST, GET");
response.addHeader("Access-Control-Allow-Headers", "Content-Type");
I tried all the way of above answers and other similar ones. But always, I came across same error message.
Finally I reach a correct answer for my situation. I use Jersey with Jetty and I am not using web.xml. If you try all methods and you don't enable the CORS support, maybe you can try this solution below.
First, define a filter (you can define another one which directly implements Filter class)
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Response;
public class CorsFilter implements ContainerRequestFilter, ContainerResponseFilter {
private static boolean isPreflightRequest(ContainerRequestContext request) {
return request.getHeaderString("Origin") != null && request.getMethod().equalsIgnoreCase("OPTIONS");
}
#Override
public void filter(ContainerRequestContext request) throws IOException {
// If it's a preflight request, we abort the request
if (isPreflightRequest(request)) {
request.abortWith(Response.ok().build());
return;
}
}
#Override
public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException {
// if there is no Origin header, we don't do anything.
if (request.getHeaderString("Origin") == null) {
return;
}
// If it is a preflight request, then we add all
// the CORS headers here.
if (isPreflightRequest(request)) {
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
response.getHeaders().add("Access-Control-Allow-Headers",
// Whatever other non-standard/safe headers (see list above)
// you want the client to be able to send to the server,
// put it in this list. And remove the ones you don't want.
"X-Requested-With,Content-Type,Content-Length,Authorization,"
+ "Accept,Origin,Cache-Control,Accept-Encoding,Access-Control-Request-Headers,"
+ "Access-Control-Request-Method,Referer,x-csrftoken,ClientKey");
}
response.getHeaders().add("Access-Control-Allow-Origin", "*");
}
}
Register this filter to resource config
import java.io.IOException;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
public class AppServer {
public static void main(String[] args) throws Exception {
Server jettyServer = new Server();
// Add port
ServerConnector jettyServerConnector = new ServerConnector(jettyServer);
jettyServerConnector.setPort(Integer.parseInt("9090"));
jettyServer.addConnector(jettyServerConnector);
// Define main servlet context handler
ServletContextHandler jettyServletContextHandler = new ServletContextHandler();
jettyServletContextHandler.setContextPath("/service");
// Define main resource (webapi package) support
ResourceConfig webapiResourceConfig = new ResourceConfig();
webapiResourceConfig.packages("com.example.service");
ServletContainer webapiServletContainer = new ServletContainer(webapiResourceConfig);
ServletHolder webapiServletHolder = new ServletHolder(webapiServletContainer);
jettyServletContextHandler.addServlet(webapiServletHolder, "/webapi/*");
// Add Cors Filter
webapiResourceConfig.register(CorsFilter.class, 1);
try {
jettyServer.start();
jettyServer.dump(System.err);
jettyServer.join();
} catch (Throwable t) {
t.printStackTrace(System.err);
} finally {
jettyServer.destroy();
}
}
}
That's it. This solution solved my problem. Maybe it can be useful for others.

Disable auto generated JAX-WS Status Page

When I deploy and run my web service developed with JAX-WS I can see a summary page with some info on it, something like in this picture:
http://www.mkyong.com/webservices/jax-ws/deploy-jax-ws-web-services-on-tomcat/
For the final implementation we would like to remove this page so that a custom or a blank page is returned while still having access to the web service endpoint.
We are currently running on Tomcat.
There is a field on the WSServlet class that might do what you are looking for: JAXWS_RI_PROPERTY_PUBLISH_STATUS_PAGE (it's value is com.sun.xml.ws.server.http.publishStatusPage).
Looking at the source code from a JAX-WS download it seems that you need to set it as a context parameter in your web.xml file:
<web-app>
<context-param>
<param-name>com.sun.xml.ws.server.http.publishStatusPage</param-name>
<param-value>false</param-value>
</context-param>
...
Seems that HttpAdapter had something similar on it but was taken from an environment variable:
setPublishStatus(
System.getProperty(HttpAdapter.class.getName() + ".publishStatusPage")
.equals("true"));
The code on HttpAdapter is marked deprecated in the javadoc so the context parameter seems the way to go.
I have been trying to solve this for two days, Glassfish 3.1.2.
The only solution was to have
-Dcom.sun.xml.ws.transport.http.HttpAdapter.publishStatusPage=false
I know its old, but wantd to maintain the knowledge. Hope this helps any one with this issue.
I have completed the same task for WebLogic recently.
It was requested to hide/show a status page of a public web service depending on a target environment i.e. hide for production, show for dev.
Nothing of the previous answers worked for me.
The success solution is based on implementation of javax.servlet.Filter.
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.HttpMethod;
#WebFilter(urlPatterns = { "/WeblogicWebService" })
public class FilterStatusSoapPage implements Filter {
#Value("${soap.status.page.disabled}")
private boolean flagDisablePublishStatusPage;
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
HttpServletRequest httpReq = (HttpServletRequest) request;
HttpServletResponse httpRes = (HttpServletResponse) response;
String queryString = httpReq.getQueryString();
if(flagDisablePublishStatusPage)
if(queryString == null || queryString.trim().isEmpty())
if(HttpMethod.GET.matches(httpReq.getMethod())) {
httpRes.setStatus(HttpServletResponse.SC_OK);
httpRes.getWriter().write("Access to status page of Web
Service is not allowed");
httpRes.getWriter().flush();
httpRes.getWriter().close();
return;
}
} catch (Exception e) {
System.err.println("Error on FilterStatusSoapPage filter");
chain.doFilter(request, response);
return;
}
chain.doFilter(request, response);
}
public void init(FilterConfig fConfig) throws ServletException {}
public void destroy() {}
}

Build Soap web service client using wsdl documents in Eclipse

I need to create web service client in Java using Eclipse the consumes the onvif wsdl.
I spent several hours without finding a how to do that, this the first time I am using soap, my experience was in REST.
I tried many tutorials like this to create web service client, but when I am trying to choose the wsdl file from my local disk, eclipse shows the an error Could not retrieve the WSDL file ..., the link structure I used for the file was file:/C:/ONVIF/media.wsdl.
I need to use any Java framework that support WS-Notification to implement my client.
Can you please tell me how to implement client web service that consumes the WSDL files.
Do I need web server to implement soap web service client?
If yes, why?
Here is a complete code and guide on how to consume one of ONVIF's wsdl files (devicemgmt.wsdl) and how to use it to connect to a device:
package test;
import java.io.IOException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.ws.Binding;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Holder;
import javax.xml.ws.Service;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import org.onvif.ver10.device.wsdl.Device;
import org.onvif.ver10.schema.DateTime;
import org.onvif.ver10.schema.SystemDateTime;
import org.onvif.ver10.schema.Time;
import com.sun.org.apache.xml.internal.security.utils.Base64;
public class OnvifTest {
private static TimeZone utc = TimeZone.getTimeZone("UTC");
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
static {
sdf.setTimeZone(utc);
}
private static long serverTime = 0;
private static long clientTime = 0;
private static final String ip = "...";
private static final String user = "...";
private static final String pass = "...";
// Some cameras (e.g. Axis) require that you set the user/pass on the ONVIF section in it's web interface
// If the camera is reset to factory defaults and was never accessed from the web, then
// either no user/pass is needed or the default user/pass can be used
#SuppressWarnings("rawtypes")
public static void main(String[] args) throws IOException {
// The altered wsdl file
URL url = new URL("file://"+System.getProperty("user.home")+"/onvif/devicemgmt.wsdl");
// This file was downloaded from the onvif website and added a mock service in order to make it complete:
// <wsdl:service name="DeviceService">
// <wsdl:port name="DevicePort" binding="tds:DeviceBinding">
// <soap:address location="http://localhost/onvif/device_service"/>
// </wsdl:port>
// </wsdl:service>
// The altered file was then used to generate java classes using $JAVA_HOME/bin/wsimport -Xnocompile -extension devicemgmt.wsdl
QName qname = new QName("http://www.onvif.org/ver10/device/wsdl", "DeviceService");
Service service = Service.create(url, qname);
Device device = service.getPort(Device.class);
BindingProvider bindingProvider = (BindingProvider)device;
// Add a security handler for the credentials
final Binding binding = bindingProvider.getBinding();
List<Handler> handlerList = binding.getHandlerChain();
if (handlerList == null)
handlerList = new ArrayList<Handler>();
handlerList.add(new SecurityHandler());
binding.setHandlerChain(handlerList);
// Set the actual web services address instead of the mock service
Map<String, Object> requestContext = bindingProvider.getRequestContext();
requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "http://"+ip+"/onvif/device_service");
// Read the time from the server
SystemDateTime systemDateAndTime = device.getSystemDateAndTime();
// Mark the local time (no need for an actual clock, the monotone counter will do just fine)
clientTime = System.nanoTime()/1000000;
// Generate the server time in msec since epoch
DateTime utcDateTime = systemDateAndTime.getUTCDateTime();
org.onvif.ver10.schema.Date date = utcDateTime.getDate();
Time time = utcDateTime.getTime();
Calendar c = new GregorianCalendar(utc);
c.set(date.getYear(), date.getMonth()-1, date.getDay(), time.getHour(), time.getMinute(), time.getSecond());
System.out.println(sdf.format(c.getTime()));
serverTime = c.getTimeInMillis();
// Now try and read something interesting
Holder<String> manufacturer = new Holder<String>();
Holder<String> model = new Holder<String>();
Holder<String> firmwareVersion = new Holder<String>();
Holder<String> serialNumber = new Holder<String>();
Holder<String> hardwareId = new Holder<String>();
device.getDeviceInformation(manufacturer, model, firmwareVersion, serialNumber, hardwareId);
System.out.println(manufacturer.value);
System.out.println(model.value);
System.out.println(firmwareVersion.value);
System.out.println(serialNumber.value);
System.out.println(hardwareId.value);
}
// Calcualte the password digest from a concatenation of the nonce, the creation time and the password itself
private static String calculatePasswordDigest(byte[] nonceBytes, String created, String password) {
String encoded = null;
try {
MessageDigest md = MessageDigest.getInstance( "SHA1" );
md.reset();
md.update( nonceBytes );
md.update( created.getBytes() );
md.update( password.getBytes() );
byte[] encodedPassword = md.digest();
encoded = Base64.encode(encodedPassword);
} catch (NoSuchAlgorithmException ex) {
}
return encoded;
}
// Calculate what time is it right now on the server
private static String localToGmtTimestamp() {
return sdf.format(new Date(System.nanoTime()/1000000 - clientTime + serverTime));
}
// This handler will add the authentication parameters
private static final class SecurityHandler implements SOAPHandler<SOAPMessageContext> {
#Override
public boolean handleMessage(final SOAPMessageContext msgCtx) {
// Indicator telling us which direction this message is going in
final Boolean outInd = (Boolean) msgCtx.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
// Handler must only add security headers to outbound messages
if (outInd.booleanValue() && clientTime!=0 && user!=null && pass!=null) {
try {
// Create the timestamp
String timestamp = localToGmtTimestamp();
// Generate a random nonce
byte[] nonceBytes = new byte[16];
for (int i=0 ; i<16 ; ++i)
nonceBytes[i] = (byte)(Math.random()*256-128);
// Digest
String dig=calculatePasswordDigest(nonceBytes, timestamp, pass);
// Create the xml
SOAPEnvelope envelope = msgCtx.getMessage().getSOAPPart().getEnvelope();
SOAPHeader header = envelope.getHeader();
if (header == null)
header = envelope.addHeader();
SOAPElement security =
header.addChildElement("Security", "wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
SOAPElement usernameToken =
security.addChildElement("UsernameToken", "wsse");
SOAPElement username =
usernameToken.addChildElement("Username", "wsse");
username.addTextNode(user);
SOAPElement password =
usernameToken.addChildElement("Password", "wsse");
password.setAttribute("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest");
password.addTextNode(dig);
SOAPElement nonce =
usernameToken.addChildElement("Nonce", "wsse");
nonce.setAttribute("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
nonce.addTextNode(Base64.encode(nonceBytes));
SOAPElement created = usernameToken.addChildElement("Created", "wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
created.addTextNode(timestamp);
} catch (final Exception e) {
e.printStackTrace();
return false;
}
}
return true;
}
// Other required methods on interface need no guts
#Override
public boolean handleFault(SOAPMessageContext context) {
// TODO Auto-generated method stub
return false;
}
#Override
public void close(MessageContext context) {
// TODO Auto-generated method stub
}
#Override
public Set<QName> getHeaders() {
// TODO Auto-generated method stub
return null;
}
}
}
I would recommend using wsimport command to generate the web service client to consume the web services.
The command can be executed from cmd prompt,
wsimport -d D:\WS-Client -extension -keep -XadditionalHeaders http://path-to-your-webserbice-wsdl-file/sampleWSDL?wsdl
After execution of the above command all the generated .class files and .java (source) files will be placed inside D:\WS-Client folder with proper package structure as mentioned in the wsdl file.
just ignore the .class files and copy entire package folder and include it in your consumer project to use it.
It will be like, you have the deployed web services in your source code. Just call the methods from the service classes and ohhla :)
The WSDL you were provided is invalid. Most likely due to the extensive documentation tags that were used in it. You can verify this by trying to load it in SoapUI. Your best bet is to contact the vendor to find out if they have a cleaner version of the WSDL they can provide you.
first you want to deploy your web service project on any server means tomcat or other.
after that use the running server WSDL file URL for create the client.

Pass request information from Servlet Filter to Web Service

I need to retrieve some information sent in the HTTP headers from within a Web Service. How can I achieve this?
It depends on the language and web service framework you want to use.
Your question title mentions "Servlet Filter" so I assume you work with a Java application container. If your ws framework does not support the of mapping request headers into value objects, you could use a Servlet Filter that processes the header and stores the information somewhere you can retrieve it later. The best option would be to put it in a request attribute. If you can't get to the HttpServletRequest later (which probably makes you ask this question), you can store it into a ThreadLocal variable, but this is trickier.
I'll give you a minimal example:
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
public class Filter implements javax.servlet.Filter {
public ThreadLocal<String> local;
#Override
public void doFilter(ServletRequest req, ServletResponse response,
FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest)req;
String bar = request.getHeader("foo");
local.set(bar);
// you can now retrieve the header value in your code with Filter.local.get()
try {
chain.doFilter(request, response);
} finally {
local.remove(); // clean up
}
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig arg0) throws ServletException {
local = new ThreadLocal<String>();
}
}
It works, but in a real life implementation you should store an Object of your own Class in the ThreadLocal (e.g. a Bean) instead of a mere String. You should consider putting the ThreadLocal variable outside your Filter (e.g. as a static variable somewhere in a more logical place).