I am building a SOAP webservice. I am using JAX-WS to create this service and deploying it on a Glassfish 3.1.2 server.
I have no problem having this service return a String build with the XML representation of what I want. I can also get it to return a specific object. What I am having issues with is streaming this resource.
This is what I have so far :
Interface :
#MTOM
#WebService
#XmlRootElement(name="root.element.class.location")
#SOAPBinding(style = Style.RPC, use=Use.LITERAL)
public interface ResultsServer {
#WebMethod
#XmlMimeType("text/xml")
public Test getResultDataAsXML(#WebParam(name="Id") Integer id) throws Exception;
}
Implementation :
---- Edit ----
This is where I would like to stream my resource. Let's say I need my results object becomes extremely large, I don't want to hold this is memory and would like to start sending it without holding it. (commented this in code)
#WebService(endpointInterface = "my.endpoint.class")
#StreamingAttachment(parseEagerly=true, memoryThreshold=4000000L)
public class ResultsServerImpl implements ResultsServer {
#Override
public Test getResultDataAsXML(Integer id) throws Exception {
Test results = new Test();
for(int i=0; i<[very large number]; i++) {
results.getResults().add("here : " + i);
/**at one point, this is too large to hold in memory
I would like to be able to start returning the object here
so it is not taking up all available memory */
}
return results; //or close the stream
}
}
---- End Edit ----
And my Test class is a simple class looking like this :
public class Test {
private ArrayList<String> results;
public Test() {
results = new ArrayList<String>();
}
public ArrayList<String> getResults() {
return results;
}
public void setResults(ArrayList<String> results) {
this.results = results;
}
}
Let's assume that this Test object becomes very big (and more complexe). I need to be able to stream this object. How would I go to proceed in streaming this.
Ideally, I would like to keep the structure of this object.
From what I have read so far, I would need to convert this object in some sort of DataHandler and return this object.
Any help is welcome! Thank you.
The JAX-WS implementation will leverage a JAXB implementation to marshal the object (most likely to a StAX XMLStreamWriter) so the output will be streamed (there won't be an XML document created in memory).
#BlaiseDoughan I think you've worded this the way I was looking for.
Yes that would be to prevent the instance of Test of fully being saved
in memory. Is there a way to do this?
If you want the data to appear in the messages as XML (as opposed to a SOAP attachment), the you could leverage JAXB's marshal events. In the beforeMarshal event you could load data into an object and then clear it in the afterUnmarshal method. Ultimately all the data will be pulled in, but it won't all be referenced at the same time.
http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/Unmarshaller.html#unmarshalEventCallback
I would recommend using the xstream (http://x-stream.github.io/) library from thoughtworks for your streaming as it is bindable on both sides of your service and is compatible with SOAP envelops. In fact there is even an integration with ActiveSOAP.
An example of a SOAP envelope wrapped xstream object can be seen at http://jira.codehaus.org/secure/attachment/19097/SoapEnvelopeTestCase.java. A full usage from jboss can be seen at https://issues.jboss.org/secure/attachment/12325534/SOAPClient.java?_sscc=t.
XStream has been used for some very large streaming processes (I've used it for some large 100+ MB text objects without issue).
Related
I got a problem with my WCF service. Here is the
[OperationContract]
[WebGet(UriTemplate = "/needs", ResponseFormat = WebMessageFormat.Json)]
List<CustomerNeed> getAllCustomerNeeds();
When I go on the page which call this service, I got this error
GET http://localhost:666/rest/Service1.svc/needs net::ERR_CONNECTION_RESET
When I'm trying to return a string instead of a List, it works.
CustomerNeed is a class generate from my database via EntityFramework.
In my service, I'm only calling an other method which is in an other class;
public List<CustomerNeed> getAllCustomerNeeds()
{
var needs = from cn in db.CustomerNeeds
select cn;
List<CustomerNeed> list = new List<CustomerNeed>();
foreach (CustomerNeed cusN in needs)
{
list.Add(cusN);
}
return list;
}
Maybe is it because I have a foreign key in my table CustomerNeed ?
When I do "LINQ to entities" to import my database, do I have to import tables that were created because of many to many relation ?
I will recommend you to create a simple custom class which will represent your CustomerNeeds database entity, initiate this object on the server side and pass to the client application. It can help you to avoid this problem and also it is recommended way to transfer data accross the WCF services.
In this case you need to do the next steps:
1) Create a public class CustomerNeeds and mark it with the DataContract attribute. For example:
[DataContract]
public class CustomerNeeds
{
[DataMember]
public SomeDataType PropertyName {get; set;}
}
2) Initiate this object on the service, change return datatype in getAllCustomerNeeds() method from the entity class to the newly created class CustomerNeed and pass this data to the clien
And that`s all.
You haven't shown where/what db is, but I'm assuming if you're using entity framework as your tag implies it's a entities context. You might be having some issues with the context already being disposed or not newed up correctly (though I would have expected you to receive a slightly different error if that's the case.)
It looks like you're going through some unnecessary steps in your function, I would think something like this would work:
public List<CustomerNeed> getAllCustomerNeeds()
{
using (var db = new YourContext()) // plug in your context object
{
return db.CustomerNeeds.ToList();
}
}
Additionally when you say it "works as a string" are you returning something small like "hello world"? you might need to take a look at your WCF configuration to make sure it can handle the amount of data you're trying to pass back and forth.
Hope this helps!
I am using Jena API to get RDF data from Allegrograph Server. I have written a REST webservice using Jersey jar to get this data.
My java code for the webservice is as shown below:
#GET
#Path("/JENA")
#Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public String getData() throws RepositoryException {
AGGraphMaker maker = new AGGraphMaker(conn);
AGGraph graph = maker.getGraph();
AGModel model = new AGModel(graph);
AGQuery agQuery = AGQueryFactory.create(query);
QueryExecution qe = AGQueryExecutionFactory.create(agQuery, model);
String result = null;
ByteArrayOutputStream byteArrayOutputStream = null;
try {
ResultSet rs = qe.execSelect();
While(rs.hasNext()){
byteArrayOutputStream = new ByteArrayOutputStream();
if("JSON".equalsIgnoreCase(outputFormat)){
ResultSetFormatter.outputAsJSON(byteArrayOutputStream, rs);
result = byteArrayOutputStream.toString();
System.out.println("Result is "+result);
} else if("XML".equalsIgnoreCase(outputFormat)){
ResultSetFormatter.outputAsXML(byteArrayOutputStream, rs);
result = byteArrayOutputStream.toString();
}else if("CSV".equalsIgnoreCase(outputFormat)){
ResultSetFormatter.outputAsCSV(byteArrayOutputStream, rs);
result = byteArrayOutputStream.toString();
}
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
I get no results for the SPARQL query when I deploy this web service on Tomcat server and test it using REST client app on Chrome and firefox.
But the same code(absolutely no difference in webservice code and this main method code) if I write in a plain java class and run its main method, i am getting 36 results. I am not sure what the issue is.
Please help me in this regard.
You need to separate the concerns:
Move the service logic - the bit that actually queries Allegro graph - to a separate class so that it's properly encapsulated. The API for the class should reflect its responsibilities in your application, not the way that it happens to be working at the moment.
Write JUnit tests for the service class. This is important - it gives you confidence that your service is performing its job correctly, and keeps on doing so as you develop your application.
Write your Jersey method to invoke any service object that conforms to the API of your service class.
Write one or more HTTPUnit (or similar) tests to invoke your REST API. Ideally, you'll use a mock or test double instead of the actual service. What you want to test is whether the HTTP request reaches the right method, and that method delegates to the service object with the right arguments. You're then testing (and debugging!) a smaller number of concerns.
It's much better to work with small units of functionality with a clear idea of what their responsibilities are. And you should definitely learn to work with tests - it's a big win in the medium term, even if it means a bit more learning up front!
hi i have requirement to capture the data for validations. i am able to fetch the data using RPCRequest and RPCManager by using setActionUrl to the controller class.from there creating the service class and dao classes .i am able to fetch the data into controller class.but i am unable get the data back into my grid.i want the data to be fetched into a variable.i am not using asynchronus service.i used async method in grid i am able to fetch into onSuccess() method.but without using how i can fetch.the data into grid.
with regards
subodh
Here example in our ServiceImpl class to retrive datas from DB
public final String getDatas(final HashMap<String, String> param) {
List<ShippingBean> result = null;
JSONObject obj = new JSONObject();
try {
// retrieve data from DB
data = dao.selectAll();
}
catch (BusinessException e) {
throw new InvocationException("BusinessException occurs ...", e);
}
obj = JSONObject.fromObject(result);
return obj.toString();
}
We use net.sf.json to serialize as DOM and return this to presenter call as AsyncCallBack method. And then , retrieve data as like that..
AsyncCallback<String> callback = new AsyncCallback<String>() {
public void onFailure(final Throwable caught) {
Window.alert("Error!");
}
public void onSuccess(final String result) {
HTML html = new HTML(result.replace(" ", "-"));
JSONValue value = JSONParser.parseLenient(html.getText());
JSONWrapper json = new JSONWrapper(value);
System.out.println(json.get(0).get("variableName").stringValue());
}
};
I have done something similar within a project with retrieve data and posting it to GWT from database. My database setup was Microsoft SQL 2012 and Hibernate framework to retrieve it. However I created a custom try/catch block and if/else bocks for validation on client side.
I used this tutorial from Hibernate to GWT to set up the transactions between the web application and the database for both saving and retrieving. Their source code provides the web page modules for setting up the displaying which I mimicked to fit to my needs since I was not storing "records" or "users".
GWT does have a validation setup but I spent a 10 hours trying to figure out the client side validation and gave up for something much simpler such as try/catch on the data being submitted since I had no concern for the format of the numbers.
Google "GWT Validation" and you should have some documentation about it but their isn't that much to choose from since everything seems to be a copy of Google's documentation.
Link - Hibernate to GWT
Hope this helps or points you in the right direction towards your answer.
Using JAX-WS 2, I see an issue that others have spoken about as well. The issue is that if a SOAP message is received inside a handler, and that SOAP message is large - whether due to inline SOAP body elements that happen to have lots of content, or due to MTOM attachments - then it is dangerously easy to get an OutOfMemoryError.
The reason is that the call to getMessage() seems to set off a chain of events that involve reading the entire SOAP message on the wire, and creating an object (or objects) representing what was on the wire.
For example:
...
public boolean handleMessage(SOAPMessageContext context)
{
// for a large message, this will cause an OutOfMemoryError
System.out.println( context.getMessage().countAttachments() );
...
My question is: is there a known mechanism/workaround for dealing with this? Specifically, it would be nice to access the SOAP part in a SOAP message without forcing the attachments (if MTOM for example) to also be vacuumed up.
For those who run their app on JBoss 6 & 7 (with Apache CXF)... I was able to troubleshoot the problem by implementing my handler from the LogicalHandler interface instead of the SOAPHandler.
In this case your handleMessage() method would get the LogicalMessageContext context (instead of SOAPMessageContext) in the arguments that has no issues with the context.getMessage() call
There's actually a JAX-WS RI (aka Metro) specific solution for this which is very effective.
See https://javaee.github.io/metro/doc/user-guide/ch02.html#efficient-handlers-in-jax-ws-ri. Unfortunately that link is now broken but you can find it on WayBack Machine. I'll give the highlights below:
The Metro folks back in 2007 introduced an additional handler type, MessageHandler<MessageHandlerContext>, which is proprietary to Metro. It is far more efficient than SOAPHandler<SOAPMessageContext> as it doesn't try to do in-memory DOM representation.
Here's the crucial text from the original blog article:
MessageHandler:
Utilizing the extensible Handler framework provided by JAX-WS
Specification and the better Message abstraction in RI, we introduced
a new handler called MessageHandler to extend your Web Service
applications. MessageHandler is similar to SOAPHandler, except that
implementations of it gets access to MessageHandlerContext (an
extension of MessageContext). Through MessageHandlerContext one can
access the Message and process it using the Message API. As I put in
the title of the blog, this handler lets you work on Message, which
provides efficient ways to access/process the message not just a DOM
based message. The programming model of the handlers is same and the
Message handlers can be mixed with standard Logical and SOAP handlers.
I have added a sample in JAX-WS RI 2.1.3 showing the use of
MessageHandler to log messages and here is a snippet from the sample:
public class LoggingHandler implements MessageHandler<MessageHandlerContext> {
public boolean handleMessage(MessageHandlerContext mhc) {
Message m = mhc.getMessage().copy();
XMLStreamWriter writer = XMLStreamWriterFactory.create(System.out);
try {
m.writeTo(writer);
} catch (XMLStreamException e) {
e.printStackTrace();
return false;
}
return true;
}
public boolean handleFault(MessageHandlerContext mhc) {
.....
return true;
}
public void close(MessageContext messageContext) { }
public Set getHeaders() {
return null;
}
}
(end quote from 2007 blog post)
You can find a full example in the Metro GitHub repo.
What JAX-WS implementation runtime are you using? If there's a way to do this using the runtime built into WebSphere I'm certain there's a way to do this cleanly in other runtimes like Axis2 (proper), Apache CXF, and Metro/RI.
I am using the other way to reduce the memory costing, which is Message Accessor.
Instead of using context.getMessage(), I changed it to this way:
Object accessor = context.get("jaxws.message.accessor");
if (accessor != null) {
baosInString = accessor.toString();
}
Base on advice from IBM website. http://www-01.ibm.com/support/docview.wss?uid=swg1PM21151
I'm having trouble setting something up that I'm pretty sure /should/ be easy, so I thought I'd throw it to the crowd. I can't seem to find what I'm looking for elsewhere on the web or on SE.
I am simplifying my project of course, but basically I have a JAX-WS annontated Jersey resource class that looks something like this:
#Path("myresource")
public class MyResource {
#Autowired
MyComplexObjectDAO daoInstance;
#Path("findObject/{id}")
#GET
public MyComplexObject findObject( #PathParam(value="id") String id ) {
return daoInstance.findObject( id );
}
#Path("saveObject")
#PUT
public MyComplexObject saveObject( MyComplexObject objectToSave ) {
MyComplexObject savedObject = daoInstance.saveObject( objectToSave );
return savedObject;
}
}
So you can see I'm autowiring a DAO object using spring, and then I use the DAO methods in the REST handlers.
The 'findObject' call seems to work fine - so far it works exactly as I expect it to.
The 'saveObject' call is not working the way I want and that's what I need some advice on.
You can see that I'm trying to directly take an instance of my complex object as a parameter to the REST method. Additionally I would like to return an instance of the complex object after it's been saved.
I put together some 'client' code for testing this out.
#Test
public void saveTest() {
WebResource wsClient = createWebServiceClient();
MyComplexObject unsavedInstance = createMyComplexObject();
MyComplexObject savedInstance =
wsClient
.path("saveObject")
.accept(MediaType.APPLICATION_XML)
.put(MyComplexObject.class, unsavedInstance);
assertNotNull(savedIntent);
}
Which is returning the following error:
com.sun.jersey.api.client.UniformInterfaceException: PUT http://localhost:8081/rest/myresource/save returned a response status of 400 Bad Request
I don't see why this isn't working and I think I've tried just about everything I can think of. Any help or direction would be very much appreciated.
Thanks so much!
I see that you call the accept() method in your test client (which means that a "Accept:" header is added to the request, indicating the server what type of representation you would like). However, you don't call the type() method to add a "Content-type:" header and inform the server that you are sending XML data. See http://jersey.java.net/nonav/documentation/latest/client-api.html#d4e644 for examples.
Side remark: your URLs are not RESTful - you should avoid verbs in your path:
So, instead of:
/api/findObject/{id}
/api/saveObject
You should use:
/api/objects/{id}
/api/objects
Last note: to create an object on calling /api/objects, you should do a POST and not a PUT to adhere to REST best practices and widely adopted patterns.
switching to the 'concrete class' solution I alluded to in my earlier comment is what fixed things up for me.