Accessing a report (SSRS 2005) from a java client built with jax-ws 2.0
Hi,
Based on jax-ws 2.0 technology, I built a java client application to access a report via the web service interface of the SQL Server Reporting Services 2005.
I used the jax-ws "wsimport" tool to generate the java classes needed to call the web services (The class generation is based on the wsdl and I used this one http://serverName/reportserver/reportexecution2005.asmx?wsdl) and wrote the following sample code :
ReportExecutionService service = new ReportExecutionService();
ReportExecutionServiceSoap rs = service.getReportExecutionServiceSoap();
String reportPath = "HelloWorld";
String format = "MHTML";
String devInfo = "<DeviceInfo><Toolbar>False</Toolbar></DeviceInfo>";
Holder<byte[]> result = new Holder<byte[]>();
Holder<String> extension = new Holder<String>();
Holder<String> mimeType = new Holder<String>();
Holder<String> encoding = new Holder<String>();
Holder<ArrayOfWarning> warnings = new Holder<ArrayOfWarning>();
Holder<ArrayOfString> streamIDs = new Holder<ArrayOfString>();
try {
ExecutionInfo execInfo = rs.loadReport(reportPath, null);
rs.render(format, devInfo, result, extension, mimeType, encoding, warnings, streamIDs);
}
catch (Exception e) {
System.out.println( e );
}
The trouble is that an exception is caught on the call to the render method
: The session identifier is missing. A session identifier is required for this operation. ---> The session identifier is missing. A session identifier is required for this operation.javax.xml.ws.soap.SOAPFaultException
I found on the web (in C# examples) that this SessionId is supposed to be contained in the ExecutionId attribute of anExecutionHeader object. The ReportExecutionServiceSoap class (my rs object in the example) does not offer any way to directly set this SessionId or to associate an ExceptionHeader before calling the render method.
Have you ever tried - and managed - to do it.
Many thanks
Answers
- Hi Folks,
just found the solution for my problem and will contribute it, if someone runs again into the same.
The problem was fact, that Metro (JAX-WS) generates this SOAP-Header for the Execution Header:
<executionHeader
xmlns="http://schemas.microsoft.com/sqlserver/2005/06/30/reporting/reportingservices">
<ExecutionID>tll4ro45jf0ju34525e3ajr0
</ExecutionID>
</executionHeader>
But the SSRS responses have this Header:
<ExecutionHeader
xmlns="http://schemas.microsoft.com/sqlserver/2005/06/30/reporting/reportingservices">
<ExecutionID>tll4ro45jf0ju34525e3ajr0
</ExecutionID>
</ExecutionHeader>
SSRS is in this case just case sensitive an ignores the lower case executionHeader Element.
Changing the XmlRootElement-Annotation to@XmlRootElement
(name="ExecutionHeader")fixed the problem.
Now I'm able to render the report and get the charts as images by using the RenderStream operation.- Marked As Answer byLukasz Pawlowski -- MSMSFT, ModeratorThursday, February 26, 2009 4:22 PM
Aranga,
sorry for my late reply but I wasn't notified about a new post.
This annotation is a class level one. My ExecutionHeader.java src looks now:
package com.microsoft.schemas.sqlserver._2005._06._30.reporting.reportingservices;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;
/**
* <p>Java class for ExecutionHeader complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* <complexType name="ExecutionHeader">
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element name="ExecutionID" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* </sequence>
* </restriction>
* </complexContent>
* </complexType>
* </pre>
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "ExecutionHeader", propOrder = {
"executionID"
})
@XmlRootElement(name="ExecutionHeader")
public class ExecutionHeader {
@XmlElement(name = "ExecutionID")
protected String executionID;
@XmlAnyAttribute
private Map<QName, String> otherAttributes = new HashMap<QName, String>();..getters and setters...
Hope this helps!
- Marked As Answer byLukasz Pawlowski -- MSMSFT, ModeratorThursday, February 26, 2009 4:22 PM
All Replies
- I've hit the same wall using jax-ws 2.1
I did find the executionID in the ExecutionInfo object returned from the loadReport call.
Anyone know how to associate an ExecutionHeader so that the render call will function? - It may be ugly but it seems to work.... I won't go through the time it took to get it going. The bread crumbs are few and far between. Suffice it to say I used WireShark to figure out what was needed by creating a C# client to make the calls then worked back from there to get the Java solution to produce the same request.
There are probably a few ways to get this to work but here's what i did.
Modified the generated ExecutionHeader object so that it is an XmlRootElement. This involved finding an object that was an XmlRootElement and making ExecutionHeader match the pattern.
In the code that needs to render do the following.
- cast the ReportExecutionServiceSoap to a WSBindingProvider
- Create an ExecutionHeader object and populate the ExecutionID with the value in the ExecutionInfo returned from the loadReport call.
- on the WSBindingProvider reference call setOutboundHeaders and feed it the new ExecutionHeader instance.
- Set the parms
- Render.
I would guess this would work for a jax-ws 2.0 environment too but I've not tried it. - Hi esgibson,
I'm back on it.
I switched to jax-ws 2.1 but still encounter troubles.
I think I have some class loading trouble and was I was wondering if you were using tomcat or an other application server ? If yes, which version (5.5 or 6.x).
Many thanks My client is J2SE so I don't know what this would take in Tomcat. I did see comments regarding some class loading issues but I didn't run into them and I don't seem to have the links anymore. They were not specific to SSRS.
If you're trying to simply render reports, have you considered doing the URL method instead of SOAP based access to SSRS? The MS SQLServer site had some articles on how structure a URL to render reports.
- I finally did it, I had to create an endorsed directory under tomcat root ant put jaxws-api.jar and jaxb-api.jar in it.
Many thanks for your help. - ah... sorry, i forgot to include that important tidbit. Congrats on getting it working.
- esgibson said:
Modified the generated ExecutionHeader object so that it is an XmlRootElement. This involved finding an object that was an XmlRootElement and making ExecutionHeader match the pattern.
In the code that needs to render do the following.- cast the ReportExecutionServiceSoap to a WSBindingProvider
- Create an ExecutionHeader object and populate the ExecutionID with the value in the ExecutionInfo returned from the loadReport call.
- on the WSBindingProvider reference call setOutboundHeaders and feed it the new ExecutionHeader instance.
- Set the parms
- Render.
Dear all,
I'm struggeling now with the same problem!I've imported the WSDL for ReportExecutionService with wsimport from Metro 1.4 and generated by this way all stubs etc.
Then, as suggested in the posting from esgibson, I've annotated the created Class ExecutionHeader with XmlRootElement:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "ExecutionHeader", propOrder = {"executionID"})
@XmlRootElement
public class ExecutionHeader {
...
After this, i followed the steps 1-5, but I'm getting still a "missing sessionID-Error".
My Code (snipped):
URL resWsdlURL = ClassLoader.getSystemResource("ReportExecution2005.wsdl");
ReportExecutionService reportExecutionService = new ReportExecutionService(resWsdlURL, new QName("http://schemas.microsoft.com/sqlserver/2005/06/30/reporting/reportingservices", "ReportExecutionService"));
setAuthenticator(args); //setting here the user and password propertys
ReportExecutionServiceSoap ress = reportExecutionService.getReportExecutionServiceSoap();
//setting the endpoint adress
((BindingProvider)ress).getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,"http://myserver/myreportserver/ReportExecution2005.asmx");
// setting some report parameters
ArrayOfParameterValue renderParameters = new ArrayOfParameterValue();
...
//load report and get executionID
ExecutionInfo execInfo = ress.loadReport(mockPath, null);
ExecutionHeader execHeader = new ExecutionHeader();
execHeader.setExecutionID(execInfo.getExecutionID());
//set the executionID
"
((WSBindingProvider)ress).setOutboundHeaders(execHeader);
//set Parameters
ress.setExecutionParameters(renderParameters, null);
//Now render...
ress.render("HTML4.0", HTMLDeviceInfo, result, extension, mimeType, encoding, warnings, streamIds);In the line with setExecutionParameters I got a SOAPFaultException: "Die Sitzungs-ID fehlt. Für diesen Vorgang ist eine Sitzungs-ID erforderlich.
(Sorry, only german error message, bit it says that the sessionId is missing)Any suggestions that I'm doing wrong here? IMO it's how i set the executionHeader in the WSBindingProvider, but don't know another way (the Metro userguide makes it similar).
Please help!
Edit:
p.S.: some info about used Libs and system:- it's a standalone J2SE app, but will be proted into a WebApp then it runs
- Metro 1.4 (JAX-WS RI 2.1.4.1, JAXB RI 2.1.7.1)
- Tested with SUN JDK 1.5.0_16 and 1.6.0_11 with same result
- Tested from Eclipse and from console (builded a JAR)
- the Libs are in the classpath (webservices-api, webservices-extra, webservices-extra-api, webservices-rt)
2nd. Edit:
here is the SOAP Request, got by debugging:<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header>
<executionHeader
xmlns="http://schemas.microsoft.com/sqlserver/2005/06/30/reporting/reportingservices">
<ExecutionID>tll4ro45jf0ju34525e3ajr0
</ExecutionID>
</executionHeader>
</S:Header>
<S:Body>
<SetExecutionParameters
xmlns="http://schemas.microsoft.com/sqlserver/2005/06/30/reporting/reportingservices">
<Parameters>
<ParameterValue>
<Name>StringParameter</Name>
<Value>Das ist ein Stringparam</Value>
</ParameterValue>
<ParameterValue>
<Name>BoolParameter</Name>
<Value>true</Value>
</ParameterValue>
<ParameterValue>
<Name>IntegerParameter</Name>
<Value>12345</Value>
</ParameterValue>
<ParameterValue>
<Name>FloatParameter</Name>
<Value>12345,556</Value>
</ParameterValue>
<ParameterValue>
<Name>DateTimeParameter</Name>
<Value>20.12.2008</Value>
</ParameterValue>
</Parameters>
</SetExecutionParameters>
</S:Body>
</S:Envelope>
- Hi Folks,
just found the solution for my problem and will contribute it, if someone runs again into the same.
The problem was fact, that Metro (JAX-WS) generates this SOAP-Header for the Execution Header:
<executionHeader
xmlns="http://schemas.microsoft.com/sqlserver/2005/06/30/reporting/reportingservices">
<ExecutionID>tll4ro45jf0ju34525e3ajr0
</ExecutionID>
</executionHeader>
But the SSRS responses have this Header:
<ExecutionHeader
xmlns="http://schemas.microsoft.com/sqlserver/2005/06/30/reporting/reportingservices">
<ExecutionID>tll4ro45jf0ju34525e3ajr0
</ExecutionID>
</ExecutionHeader>
SSRS is in this case just case sensitive an ignores the lower case executionHeader Element.
Changing the XmlRootElement-Annotation to@XmlRootElement
(name="ExecutionHeader")fixed the problem.
Now I'm able to render the report and get the charts as images by using the RenderStream operation.- Marked As Answer byLukasz Pawlowski -- MSMSFT, ModeratorThursday, February 26, 2009 4:22 PM
- Hi MrPrinz,
I'm having same issue. where should I add this @XmlRootElement(name="ExecutionHeader"). I'm getting XmlRootElement cannot be receiv error in ExecutionHeader.java.
thanks
Aranga Aranga,
sorry for my late reply but I wasn't notified about a new post.
This annotation is a class level one. My ExecutionHeader.java src looks now:
package com.microsoft.schemas.sqlserver._2005._06._30.reporting.reportingservices;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;
/**
* <p>Java class for ExecutionHeader complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* <complexType name="ExecutionHeader">
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element name="ExecutionID" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* </sequence>
* </restriction>
* </complexContent>
* </complexType>
* </pre>
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "ExecutionHeader", propOrder = {
"executionID"
})
@XmlRootElement(name="ExecutionHeader")
public class ExecutionHeader {
@XmlElement(name = "ExecutionID")
protected String executionID;
@XmlAnyAttribute
private Map<QName, String> otherAttributes = new HashMap<QName, String>();..getters and setters...
Hope this helps!
- Marked As Answer byLukasz Pawlowski -- MSMSFT, ModeratorThursday, February 26, 2009 4:22 PM
- thanks,
I was able to get it work. Another question (sorry). How can I pass the user id and password. In your code its setAuthenticator(args);
thanks
Aranga Hi Aranga,
ok, how to work with the setAuthenticator:
First, you have to extend the class java.net.Authenticator (see the Javadoc). Just only two fields username and password and you have to override the method getPasswordAuthentification. Very simple.
Now, create a new instance of your Authenticator-Instance and use the static setDefault-Method from java.net.Authenticator:
SSRSAuthenticator(wsUser,wsPassword);Authenticator myAuth = new
Authenticator.setDefault(myAuth);This works only with Metro, not with the JAX-WS impl in Axis 2 (but not dived deep in it why not)
BTW, be aware, that the JAX-WS implementation in Metro is not threadsafe!
If you have threads calling concurrently your WS then you have two choices:
- you can use synchronisation (synchronised blocks or using reentrant locks)
- use a object cache with a a lot of dedicated ws proxys (my way, I'm using commons pooling)
Best regards
Prinz- Edited byMrPrinz Friday, February 27, 2009 8:52 AMonly spelling
- What method in JAX-WS is not thread safer.. This is my code.
Authenticator.setDefault(NTLM_AUTHENTICATOR);
ReportExecutionServiceSoap execSoap = REPORT_EXECUTION_SERVICE
.getReportExecutionServiceSoap();
ExecutionInfo info = execSoap.loadReport(getReportName(), null);
ExecutionHeader executionHeader = new ExecutionHeader();
executionHeader.setExecutionID(info.getExecutionID());
((com.sun.xml.ws.developer.WSBindingProvider) execSoap)
.setOutboundHeaders(executionHeader);
execSoap.setExecutionParameters(param, "en-us");
execSoap.render(getReportType(), deviceInfo, result, extension, mimeType,
encoding, warnings, streamIds);
REPORT_EXECUTION_SERVICE is a static variable (created at the time of App strat) and I'm re-using it. Is it okey. Or I have to put
every thing into ThreadLocal .
thanks
Aranga - Aranga,
the ReportExecutionServiceSoap-Objects are not thread safe. If you reuse these, you get a problem... The way you are doing it is AFAIK ok, but has a disadvantage:
the creation of this object is very "expensive" in the meaning of time (e.g. depends on there you have the wsdls located).
Due to this, I create a bunch of them and put them into a object cache so that I can reuse them threadsafe.

