Here is an example of my webservice:
#Override
#RequestMapping(value="{width}/{height}/{limit}", method = RequestMethod.POST)
public #ResponseBody ThumbnailResponse createThumbnails(#PathVariable int width, #PathVariable int height, #PathVariable int limit) {
List<String> list = thumbnailService.processImages(width,height,limit);
return new ThumbnailResponse(list.size(),list);
}
What I want to do is to answer a 400 BAD REQUEST to the user if the value of width or height is less than 10.
I need a kind of "REST Validator" before sending the response to the user. How can I do that ?
Thanks.
The approach I would follow is based on Spring MVC exception handling
What it means in practice is that:
You make a custom exception
Change your controller method accordingly
Catch the exception in your exception handler, and set the proper status and message
In terms of code it would mean
public class HeightNotAllowedException extends Exception {
}
Change in the controller
#Override
#RequestMapping(value="{width}/{height}/{limit}", method = RequestMethod.POST)
public #ResponseBody ThumbnailResponse createThumbnails(#PathVariable int width, #PathVariable int height, #PathVariable int limit) throws HeightNotAllowedException {
if (height < 10) {
throw new HeightNotAllowedException();
}
List<String> list = thumbnailService.processImages(width,height,limit);
return new ThumbnailResponse(list.size(),list);
}
Global exception handler (though you can go for per controller one)
#ControllerAdvice
public class GlobalErrorHandler {
#ExceptionHandler(value = HeightNotAllowedException.class)
#ResponseBody
#ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "Invalid Height")
public String heightError(HeightNotAllowedException ex) {
LOGGER.error("Invalid height");
return "Invalid Height";
}
}
Although there are couple of ways, for this case you can do just like below.
#Override
#RequestMapping(value="{width}/{height}/{limit}", method = RequestMethod.POST)
public #ResponseBody ResponseEntity createThumbnails(#PathVariable int width, #PathVariable int height, #PathVariable int limit) {
Gson gson = new Gson();
if(width <10 || height <10){
return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
}
List<String> list = thumbnailService.processImages(width,height,limit);
ThumbnailResponse response = new ThumbnailResponse(list.size(),list);
return new ResponseEntity<String>(gson.toJson(response), HttpStatus.OK);
}
However the standard way is to write Validator for the object and there is a method validate,Inside which you can write your validation logic and
class YourValidator implements Validator{
public void validate(Object target, Errors errors) {
//write your validation logic
}
}
and from your controller you can use it and validate the response like below.
if(error.hasErrors()){
return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
}
Related
I am trying to use aws sdk2 java for s3 select operations but not able to get extract the final data. Looking for an example if someone has implemented it. I got some idea from [this post][1] but not able to figure out how to get and read the full data .
Fetching specific fields from an S3 document
Basically, equivalent of v1 sdk:
``` InputStream resultInputStream = result.getPayload().getRecordsInputStream(
new SelectObjectContentEventVisitor() {
#Override
public void visit(SelectObjectContentEvent.StatsEvent event)
{
System.out.println(
"Received Stats, Bytes Scanned: " + event.getDetails().getBytesScanned()
+ " Bytes Processed: " + event.getDetails().getBytesProcessed());
}
/*
* An End Event informs that the request has finished successfully.
*/
#Override
public void visit(SelectObjectContentEvent.EndEvent event)
{
isResultComplete.set(true);
System.out.println("Received End Event. Result is complete.");
}
}
);```
///IN AWS SDK2, how do get ResultOutputStream ?
```public byte[] getQueryResults() {
logger.info("V2 query");
S3AsyncClient s3Client = null;
s3Client = S3AsyncClient.builder()
.region(Region.US_WEST_2)
.build();
String fileObjKeyName = "upload/" + filePath;
try{
logger.info("Filepath: " + fileObjKeyName);
ListObjectsV2Request listObjects = ListObjectsV2Request
.builder()
.bucket(Constants.bucketName)
.build();
......
InputSerialization inputSerialization = InputSerialization.builder().
json(JSONInput.builder().type(JSONType.LINES).build()).build()
OutputSerialization outputSerialization = null;
outputSerialization = OutputSerialization.builder().
json(JSONOutput.builder()
.build()
).build();
SelectObjectContentRequest selectObjectContentRequest = SelectObjectContentRequest.builder()
.bucket(Constants.bucketName)
.key(partFilename)
.expression(query)
.expressionType(ExpressionType.SQL)
.inputSerialization(inputSerialization)
.outputSerialization(outputSerialization)
.scanRange(ScanRange.builder().start(0L).end(Constants.limitBytes).build())
.build();
final DataHandler handler = new DataHandler();
CompletableFuture future = s3Client.selectObjectContent(selectObjectContentRequest, handler);
//hold it till we get a end event
EndEvent endEvent = (EndEvent) handler.receivedEvents.stream()
.filter(e -> e.sdkEventType() == SelectObjectContentEventStream.EventType.END)
.findFirst()
.orElse(null);```
//Now, from here how do I get the response bytes ?
///////---> ISSUE: How do I get ResultStream bytes ????
return <bytes>
}```
// handler
private static class DataHandler implements SelectObjectContentResponseHandler {
private SelectObjectContentResponse response;
private List receivedEvents = new ArrayList<>();
private Throwable exception;
#Override
public void responseReceived(SelectObjectContentResponse response) {
this.response = response;
}
#Override
public void onEventStream(SdkPublisher<SelectObjectContentEventStream> publisher) {
publisher.subscribe(receivedEvents::add);
}
#Override
public void exceptionOccurred(Throwable throwable) {
exception = throwable;
}
#Override
public void complete() {
}
} ```
[1]: https://stackoverflow.com/questions/67315601/fetching-specific-fields-from-an-s3-document
i came to your post since I was working on the same issue as to avoid V1.
After hours of searching i ended up with finding the answer at. https://github.com/aws/aws-sdk-java-v2/pull/2943/files
The answer is located at SelectObjectContentIntegrationTest.java File
services/s3/src/it/java/software/amazon/awssdk/services/SelectObjectContentIntegrationTest.java
The way to get the bytes is by using the RecordsEvent class, please note for my use case I used CSV, not sure if this would be different for a different file type.
in the complete method you have access to the receivedEvents. this is where you get the first index to get the filtered returned results and casting it to the RecordsEvent class. then this class provides the payload as bytes
#Override
public void complete() {
RecordsEvent records = (RecordsEvent) this.receivedEvents.get(0)
String result = records.payload().asUtf8String();
}
History:
A WSDL was provided to me with which I generated a service reference.
The object to serialize and send to the web service to consume, was a strongly, complex-typed class.
Instead of sending a strongly, complex-typed class, I wanted to send an XMLDocument instead.
So I modified the service and I am left with this:
[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.17929")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(Name = "B2MML_ProcessProductionPerformance_MaterialConsumption_OBBinding", Namespace="http://company.com/M2D/Manufacturing/ManufacturingExecution/GoodsIssue/pi")]
public partial class B2MML_ProcessProductionPerformance_MaterialConsumption_OBService : System.Web.Services.Protocols.SoapHttpClientProtocol {
private System.Threading.SendOrPostCallback B2MML_ProcessProductionPerformance_MaterialConsumption_OBOperationCompleted;
public B2MML_ProcessProductionPerformance_MaterialConsumption_OBService()
{
}
public event B2MML_ProcessProductionPerformance_MaterialConsumption_OBCompletedEventHandler B2MML_ProcessProductionPerformance_MaterialConsumption_OBCompleted;
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://sap.com/xi/WebService/soap1.1", OneWay=true, Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Bare)]
public void B2MML_ProcessProductionPerformance_MaterialConsumption_OB(XmlDocument ProcessProductionPerformance)
{
this.Invoke("B2MML_ProcessProductionPerformance_MaterialConsumption_OB", new object[] {
ProcessProductionPerformance});
}
public System.IAsyncResult BeginB2MML_ProcessProductionPerformance_MaterialConsumption_OB(XmlDocument ProcessProductionPerformance, System.AsyncCallback callback, object asyncState)
{
return this.BeginInvoke("B2MML_ProcessProductionPerformance_MaterialConsumption_OB", new object[] {
ProcessProductionPerformance}, callback, asyncState);
}
public void EndB2MML_ProcessProductionPerformance_MaterialConsumption_OB(System.IAsyncResult asyncResult)
{
this.EndInvoke(asyncResult);
}
public void B2MML_ProcessProductionPerformance_MaterialConsumption_OBAsync(XmlDocument ProcessProductionPerformance)
{
this.B2MML_ProcessProductionPerformance_MaterialConsumption_OBAsync(ProcessProductionPerformance, null);
}
public void B2MML_ProcessProductionPerformance_MaterialConsumption_OBAsync(XmlDocument ProcessProductionPerformance, object userState)
{
if ((this.B2MML_ProcessProductionPerformance_MaterialConsumption_OBOperationCompleted == null))
{
this.B2MML_ProcessProductionPerformance_MaterialConsumption_OBOperationCompleted = new System.Threading.SendOrPostCallback(this.OnB2MML_ProcessProductionPerformance_MaterialConsumption_OBOperationCompleted);
}
this.InvokeAsync("B2MML_ProcessProductionPerformance_MaterialConsumption_OB", new object[] {
ProcessProductionPerformance}, this.B2MML_ProcessProductionPerformance_MaterialConsumption_OBOperationCompleted, userState);
}
private void OnB2MML_ProcessProductionPerformance_MaterialConsumption_OBOperationCompleted(object arg)
{
if ((this.B2MML_ProcessProductionPerformance_MaterialConsumption_OBCompleted != null))
{
System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg));
this.B2MML_ProcessProductionPerformance_MaterialConsumption_OBCompleted(this, new System.ComponentModel.AsyncCompletedEventArgs(invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState));
}
}
public new void CancelAsync(object userState)
{
base.CancelAsync(userState);
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.17929")]
public delegate void B2MML_ProcessProductionPerformance_MaterialConsumption_OBCompletedEventHandler(object sender, System.ComponentModel.AsyncCompletedEventArgs e);
Problem Decription:
I am able to send an XMLDocument successfully, however, if my XMLDocument looks like this:
<ProcessProductionPerformance xmlns="SomeNS">
<ApplicationArea>Some data here</ApplicationArea>
<DataArea>Some data there</DataArea>
</ProcessProductionPerformance>
I am seeing the below in the SOAP Body (seen via Fiddler):
<ProcessProductionPerformance>
<ProcessProductionPerformance xmlns="SomeNS">
<ApplicationArea>Some data here</ApplicationArea>
<DataArea>Some data there</DataArea>
</ProcessProductionPerformance>
</ProcessProductionPerformance>
Information:
1) I do not have access to modify the web service
2) I've tried to pack XMLDocument ApplicationArea and XMLDocument DataArea individually into a class and try to serialize the class, I end up with this in the SOAP Body:
<ProcessProductionPerformance xmlns="SomeNS">
<ApplicationArea>
<ApplicationArea xmlns="SomeNS">Some data here</ApplicationArea>
<ApplicationArea>
<DataArea>
<DataArea xmlns="SomeNS">Some data there</DataArea>
<DataArea>
</ProcessProductionPerformance>
3) I suspect it's to do with the SOAPBindingUse or SOAPBindingStyle perhaps? I did not change this at all because I don't know much about it.
4) I'm just a kid new to C#.. Please have mercy.
EDIT:
Okay it seems that the enums SoapBindingUse.Literal and SoapParameterStyle.Bare have a major role to play in this. But I'm still stuck because my use of this has been correct all along.
https://msdn.microsoft.com/en-us/library/vstudio/2b4bx2t6%28v=vs.100%29.aspx
So what am I missing?
By the way, not sure if you humans care about the client but I'm calling the service like this:
var processProductionPerformance = new XmlDocument();
processProductionPerformance.LoadXml(#xmlText);
var sendPerformanceToSap = new B2MML_ProcessProductionPerformance_MaterialConsumption_OBService
{
//Url = Link here,
//Credentials since
};
sendPerformanceToSap.B2MML_ProcessProductionPerformance_MaterialConsumption_OBAsync(processProductionPerformance);
Searched quite a bit. The problem with these errors is that while the text might appear the same, the problem is always different.
My service takes ONE string value and returns a string response. Here is my code:
private class UploadStats extends AsyncTask<String, Void, String>
{
private static final String WSDL_TARGET_NAMESPACE = "http://tempuri.org/";
private static final String SOAP_ADDRESS = "http://192.168.1.101/rss/RSS_Service.asmx?WSDL";
private static final String INSERTURLACTION = "http://192.168.1.101/rss/RSS_Service.asmx/InsertURL";
private static final String INSERTURLMETHOD = "InsertURL";
#Override
protected String doInBackground(String... url)
{
String status = "";
SoapObject request = new SoapObject(WSDL_TARGET_NAMESPACE,
INSERTURLMETHOD);
request.addProperty("url", "www.yahoo.com");
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
HttpTransportSE httpTransport = new HttpTransportSE(SOAP_ADDRESS);
try
{
httpTransport.call(INSERTURLACTION, envelope);
SoapObject response = (SoapObject) envelope.bodyIn;
status = response.getProperty(0).toString();
}
catch (Exception exception)
{
Log.d("Error", exception.toString());
}
if(status.equals("1"))
{
Log.d("InsertURL", "New URL Inserterd");
}
else if(status.equals("0"))
{
Log.d("InsertURL", "URL Exists. Count incremented");
}
else
{
Log.d("InsertURL", "Err... No");
}
return status;
}
}
I get the error:
java.lang.classcastexception org.ksoap2.SoapFault
What am I doing wrong? If any more details are needed, I can add them.
The error was related to the webservice.
An incorrect namespace on the service side can cause this error (as can a lot of other problems).
Best way to check is to run the webservice on the local machine (where the service is hosted).
I am logging RequestXML for a webservice client using SoapHandler as follows
public boolean handleMessage(SOAPMessageContext smc) {
logToSystemOut(smc);
return true;
}
private void logToSystemOut(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean)
smc.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
out.println("\nOutbound message:");
} else {
out.println("\nInbound message:");
}
SOAPMessage message = smc.getMessage();
try {
message.writeTo(out);
out.println("");
} catch (Exception e) {
out.println("Exception in handler: " + e);
}
}
Got a new requirenment to add this xml to DB along with some extra values(which are not present in the xml). Is there any way I can pass few additional fields to above soap handler (in handleMessage method)?
Please note that changing the xml/WSDL or adding this to SOAP message header is not an option for me as it is owned by other interface. Any other solution?
Thanks!
You can cast your service class to a class of type "BindingProvider". In this form you can use it to assign it objects which you can access later from your SOAPHandler. Another useful usage is that you also can change the endPoint URL this way.
Before calling the service you do:
MySoapServicePortType service = new MySoapService().getMySoapServicePort();
BindingProvider bp = (BindingProvider)service;
MyTransferObject t = new MyTransferObject();
bp.getRequestContext().put("myTransferObject", t);
TypeResponse response = service.doRequest();
SOAPMessage message = t.getRequestMessage(message);
From your logging function you do:
private void logToSystemOut(SOAPMessageContext smc) {
...
MyTransferObject t = (MyTransferObject) messageContext.get("myTransferObject");
if (outboundProperty.booleanValue())
t.setRequestMessage(message);
else
t.setResponseMessage(message);
...
}
I'm trying to write my first code with Apache Camel right now. I try to follow the examples from Camel in Action, but I want to use my own example data.
What I want to do
Right now I want to read from a CSV file and get each line as a java bean.
Here is my junit test:
#Test
public void testCsvWithBindy() throws Exception {
MockEndpoint mock = getMockEndpoint("mock:queue.csv");
mock.expectedMessageCount(2);
assertMockEndpointsSatisfied();
CsvBean line1 = mock.getReceivedExchanges().get(0).getIn()
.getBody(CsvBean.class);
assertEquals("row 01", line1.getFirst());
}
public RouteBuilder createRoute() {
return new RouteBuilder() {
public void configure() throws Exception {
context.setTracing(true);
from("file://src/test/resources?noop=true&fileName=test.csv")
.unmarshal().bindy(BindyType.Csv, "my.package.for.csvrecord")
.to("mock:queue.csv");
}
};
}
The CSV contains this:
row 01,row 02,,row 04
row 11, row 12, row 13, row 14
And this is my CsvRecord:
#CsvRecord(separator = ",")
public class CsvBean {
#DataField(pos = 1)
private String first;
#DataField(pos = 2)
private String second;
#DataField(pos = 3)
private String third;
#DataField(pos = 4)
private String fourth;
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
public String getSecond() {
return second;
}
public void setSecond(String second) {
this.second = second;
}
public String getThird() {
return third;
}
public void setThird(String third) {
this.third = third;
}
public String getFourth() {
return fourth;
}
public void setFourth(String fourth) {
this.fourth = fourth;
}
}
My Problem
When I run this test, the context is started and the route is loaded. But nothing is coming through. After about 10s the context is automatically stopped and my test fails. This is the stacktrace:
java.lang.AssertionError: mock://queue.csv Received message count. Expected: <2> but was: <0>
at org.apache.camel.component.mock.MockEndpoint.fail(MockEndpoint.java:1086)
at org.apache.camel.component.mock.MockEndpoint.assertEquals(MockEndpoint.java:1068)
at org.apache.camel.component.mock.MockEndpoint.doAssertIsSatisfied(MockEndpoint.java:367)
at org.apache.camel.component.mock.MockEndpoint.assertIsSatisfied(MockEndpoint.java:346)
at org.apache.camel.component.mock.MockEndpoint.assertIsSatisfied(MockEndpoint.java:334)
at org.apache.camel.component.mock.MockEndpoint.assertIsSatisfied(MockEndpoint.java:172)
at org.apache.camel.test.junit4.CamelTestSupport.assertMockEndpointsSatisfied(CamelTestSupport.java:391)
at my.package.for.unittests.CsvToBeanWithBindyTest.testCsvWithBindy(CsvToBeanWithBindyTest.java:20)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
Need help with
I guess I'm missing something obvious, maybe something that has to do with the test setup and not so much with my CsvRecord or my route. Can you give me a tip or maybe an URL to a better tutorial? The book is not very helpful at this point... :-(
Again, right after posting my question, I found the answer myself. ;-) Here is a working junit test:
public class CsvToBeanWithBindyTest extends CamelTestSupport {
#Test
public void testCsv() throws Exception {
MockEndpoint mock = getMockEndpoint("mock:queue.csv");
mock.expectedMessageCount(1);
assertMockEndpointsSatisfied();
List line1 = (List) mock.getReceivedExchanges().get(0).getIn()
.getBody();
Map map1 = (Map) line1.get(0);
CsvBean csv1 = (CsvBean) map1.get("my.package.CsvBean");
assertEquals("row 01", csv1.getFirst());
Map map2 = (Map) line1.get(1);
CsvBean csv2 = (CsvBean) map2.get("my.package.CsvBean");
assertEquals("row 11", csv2.getFirst());
}
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
context.setTracing(true);
from("file://src/test/resources?noop=true&fileName=test.csv")
.unmarshal(new BindyCsvDataFormat("my.package"))
.to("mock:queue.csv");
}
};
}
}
The unexpected thing for me is that I get a List from my endpoint route which in turn holds many Maps. Each map has a key my.package.MyBeanClass with the value set to the actual unmarshalled row from my CSV file.