Reading XML Files using Java, return in a SOAP request - spring

I'm fairly new to using SOAP and Blueprint (Which is just like Spring).
Anyway, I'm just trying to learn the basics atm, doing pretty well so far.
I've run into a small problem when using a Java Class to retrieve a specific node value from an XML file. This works when I run the application as a stand-alone but when I am getting the request using Soap, the value "lastName" returns null.
public static void main(String[] args) throws XPathExpressionException {
DocumentBuilderFactory builderFactory =
DocumentBuilderFactory.newInstance();
DocumentBuilder builder = null;
try {
builder = builderFactory.newDocumentBuilder();
} catch (ParserConfigurationException p) {
p.printStackTrace();
}
try {
Document document = builder.parse(new FileInputStream("d:\\input11.xml"));
XPath xP = XPathFactory.newInstance().newXPath();
String expression ="/people/person/lastName";
NodeList nodeList = (NodeList) xP.compile(expression).evaluate(document, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); i++) {
lastName += nodeList.item(i).getFirstChild().getNodeValue() + " ";
}
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException s) {
s.printStackTrace();
}
System.out.println(lastName);
}
public static String returnLastName(String input){
System.out.println(lastName);
return "LastName: "+lastName +"\n";
}
}
And here's my blueprint.xml code:
<bean id="lastNameBean" class="com.*****.camelBlueprintTest.XMLCamel" />
<route id="lastName">
<from uri="cxf:bean:returnLName" />
<bean ref="lastNameBean" method="returnLastName" />
<log message="The message contains ${body}" />
<to uri="mock:result" />
</route>
So it does actually return the last names when I run the Java application, but in the SOAP request I am getting "LastName: null".

AH!! I've found the error. Silly me. So, I was calling the method in my blueprint "returnLastName" and it was returning null, I didn't realize that this method was called ALONE, so moving my code from the main into the method fixed it like a charm haha.
I feel really silly but it's always the little mistakes that get me.

Related

Spring boot - Server did not recognize the value of HTTP Header SOAPAction

I want to consume soap service using jaxb.
The generated request from jaxb is
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns2:Add xmlns:ns2="http://tempuri.org/">
<ns2:intA>10</ns2:intA><ns2:intB>20</ns2:intB>
</ns2:Add>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
But the response is a soap exception as stated in the title.
Caused by: org.springframework.ws.soap.client.SoapFaultClientException: System.Web.Services.Protocols.SoapException: Server did not recognize the value of HTTP Header SOAPAction: .
at System.Web.Services.Protocols.Soap11ServerProtocolHelper.RouteRequest()
at System.Web.Services.Protocols.SoapServerProtocol.RouteRequest(SoapServerMessage message)
at System.Web.Services.Protocols.SoapServerProtocol.Initialize()
at System.Web.Services.Protocols.ServerProtocol.SetContext(Type type, HttpContext context, HttpRequest request, HttpResponse response)
Below is my soap config code. Source example: https://howtodoinjava.com/spring-boot/spring-soap-client-webservicetemplate/
public class ConsumeSoapApplication {
public static String wsdlurl = "http://www.dneonline.com/calculator.asmx?wsdl";
public static void main(String[] args) {
try {
JAXBContext.newInstance(com.dxc.service.soap.service.calc.ObjectFactory.class.getPackage().getName(),
com.dxc.service.soap.service.calc.ObjectFactory.class.getClassLoader());
} catch (JAXBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
SpringApplication.run(ConsumeSoapApplication.class, args);
}
#Bean
CommandLineRunner lookup(SoapConnector soapConnector) {
return args -> {
Integer a = 10;
Integer b = 20;
if(args.length>0){
a = Integer.parseInt(args[0]);
b = Integer.parseInt(args[1]);
}
Add add = new Add();
add.setIntA(a);
add.setIntB(b);
AddResponse addRes = (AddResponse) soapConnector.callWebService(wsdlurl, add);
System.out.println("Got Response As below ========= : ");
System.out.println("Added result : "+addRes.getAddResult());
};
}
}
#Configuration
public class SoapConfig {
#Bean
public Jaxb2Marshaller marshaller() {
try {
JAXBContext jb = JAXBContext.newInstance(com.dxc.service.soap.service.calc.ObjectFactory.class.getPackage().getName(),
com.dxc.service.soap.service.calc.ObjectFactory.class.getClassLoader());
//Jaxb2Marshaller marshaller = (Jaxb2Marshaller) jb.createMarshaller();
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan("com.dxc.service.soap.service.calc");
//marshaller.setContextPath("com.dxc.service.soap.calc");
return marshaller;
} catch (JAXBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#Bean
public SoapConnector soapConnector(Jaxb2Marshaller marshaller) {
SoapConnector client = new SoapConnector();
client.setDefaultUri("http://www.dneonline.com/calculator.asmx");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
Please help me. Thanks.
The issue you're facing is that the web service at http://www.dneonline.com/calculator.asmx expects a SOAPAction header. And since you're not providing one the service have no idea how to route the request.
The tutorial you're following doesn't require the SOAPAction header to do the routing.
If you take a look at how the Add operation is specified in the the WSDL, you'll find the expected value of the SOAPAction header there. Same for all the other operations the service provides.
<wsdl:operation name="Add">
<soap:operation soapAction="http://tempuri.org/Add" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
Assuming that your SoapConnector class is identical to the one in the tutorial you can remove the String url as input to the callWebservice method since that is already set via the client.setDefaultUri("http://www.dneonline.com/calculator.asmx"); in the SoapConnector bean. Instead, add String soapAction as an input parameter, giving you the following
public class SOAPConnector extends WebServiceGatewaySupport {
public Object callWebService(Object request, String soapAction){
return getWebServiceTemplate().marshalSendAndReceive(url, new SoapActionCallback(soapAction));
}
}
Then, remove the wsdlurl as input to soapConnector.callWebService (it was wrong anyway) and add the soapHeader value for the operation you want to use instead, leaving you with this
#Bean
CommandLineRunner lookup(SoapConnector soapConnector) {
return args -> {
Integer a = 10;
Integer b = 20;
if(args.length>0){
a = Integer.parseInt(args[0]);
b = Integer.parseInt(args[1]);
}
Add add = new Add();
add.setIntA(a);
add.setIntB(b);
AddResponse addRes = (AddResponse) soapConnector.callWebService(add, "http://tempuri.org/Add");
System.out.println("Got Response As below ========= : ");
System.out.println("Added result : "+addRes.getAddResult());
};
}
Of course, if you want to use other operations besides Add you'll have to tweak this solution to make it generic.
#Misantorp answer is solution to call SOAP webservice where action has to be passed.
I used the same advise and could call temperature converter of w3School.
[a link] https://github.com/suhelm/SPRINGBOOT/tree/main/SOAP_temparature_converter

camel with twitter simple example

I am novice in apache-camel and I started with simple apache camel-twitter example using spring. I am using 2.20 version for camel and 2.19 version for camel-twitter. Below is my router code,
public class TwitterRouter extends RouteBuilder {
public void configure() throws Exception {
System.out.println("Test");
String twitter = "twitter://streaming/filter?type=event&keywords="+ URLEncoder.encode("london", "utf8")+"&consumerKey=key&consumerSecret=secretkey&accessToken=accesstoken&accessTokenSecret=accesstokensecret";
from(twitter).process(new Processor() {
public void process(Exchange exchange) throws Exception {
Status status = exchange.getIn().getBody(Status.class);
ProducerTemplate template = exchange.getContext().createProducerTemplate();
User user = status.getUser();
String name = user.getName();
System.out.println("name "+name);
template.sendBody("twitter","name "+name);
String screenName = user.getScreenName();
String text = status.getText();
}
});
System.out.println("Test1");
}
And my spring context file as below,
<bean id="routeBuilder" class="com.xyz.route.TwitterRouter" />
<camelContext xmlns="http://camel.apache.org/schema/spring">
<routeBuilder ref="routeBuilder" />
</camelContext>
So below are my queries,
After running program only Test and Test1 messages are printed on console. But whatever inside process method is not printed on console. I tried with producttemplate also but its not printing. So can anybody please help.
Also I tried to search for simpe camel-twitter example but not found. Does anybody have such example.

How to externalize the queries to xml files using spring

I am using spring and their JDBC template to do read/write operations to the database. I am facing a problem in my reporting module that i have to frequently change the query sqls to cater to frequent changes.
Though using spring jdbc ORM, is there a way to externalize my query parameters such that i just change it in the XML & restart and there is no need to rebuild my source again for deployment. Any approach ORM (preferred) or simple Sql will do.
As of now i have to change the query again and again ,rebuild the source and deploy.
I am not sure if Spring provides some out of the box solutions to implement what you want. But here is one way to get it done, which i had implemented ones. So i will try to reduce some hardwork for you.
You might need to implement a utility to load from resources xml file. Something like this.
public final class LoadFromResourceFileUtils {
public static String loadQuery(final String libraryPath,
final String queryName) {
final InputStream is = StreamUtils
.streamFromClasspathResource(libraryPath);
if (is == null) {
throw new RuntimeException(String.format(
"The SQL Libary %s could not be found.", libraryPath));
}
final Document doc = XMLParseUtils.parse(is);
final Element qryElem = (Element) doc.selectSingleNode(String.format(
"SQLQueries/SQLQuery[#name='%s']", queryName));
final String ret = qryElem == null ? null : qryElem.getText();
return ret;
}
}
You would need to store your queries in an XML say queries.xml and keep it in your classpath, for e.g
<?xml version="1.0" encoding="UTF-8"?>
<SQLQueries>
<SQLQuery name="myQuery">
<![CDATA[
your query
]]>
</SQLQuery>
</SQLQueries>
And in your DAO you can do this to get the query
String query = LoadFromResourceFileUtils.loadQuery(
"queries.xml", "myQuery");
XMLParseUtils and StreamUtils for your reference
public final class XMLParseUtils {
public static Document parse(final InputStream inStream) {
Document ret = null;
try {
if (inStream == null) {
throw new RuntimeException(
"XML Input Stream for parsing is null");
}
final SAXReader saxReader = new SAXReader();
ret = saxReader.read(inStream);
} catch (final DocumentException exc) {
throw new RuntimeException("XML Parsing error", exc);
}
return ret;
}
}
public final class StreamUtils {
public static InputStream streamFromClasspathResource(
final String resourceClassPath) {
final Class<StreamUtils> clazz = StreamUtils.class;
final ClassLoader clLoader = clazz.getClassLoader();
final InputStream inStream = clLoader
.getResourceAsStream(resourceClassPath);
if (inStream == null) {
if(LOGGER.isDebugEnabled()){
LOGGER.debug(String.format("Resource %s NOT FOUND.",
resourceClassPath));
}
}
return inStream;
}
}

Struts2 validating 3 times on single textfield

I am having really upsetting issue with Struts(2.2.3). Here is my field validations on ActionName-validation.xml
<field name="txtRequestDateFrom">
<field-validator type="conversion">
<param name="repopulateField">false</param>
<message>${getText("E011", {"Date from"})}</message>
</field-validator>
</field>
I don't have validate() method in my action class. And I have this in my action class:
private Date txtRequestDateFrom;
{getter, setters}
When I enter letters on my txtRequestDateFrom field I get 3 validation messages on
<s:fielderror fieldName="txtRequestDateFrom"/>
It look like this
Invalid field value for field "txtRequestDateFrom".
Invalid field value for field "txtRequestDateFrom".
Date from has an invalid value
I have my custom theme, and I am sure there is not any much modification from SIMPLE theme. My interceptor stack is pretty much as same default value stack.
<interceptor-stack name="defaultStack">
<interceptor-ref name="security"/>
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="debugging"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUploadStack" />
<interceptor-ref name="fileUpload" >
<param name="maximumSize">4000000</param>
</interceptor-ref>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError" />
<interceptor-ref name="validation">
<param name="excludeMethods">execute, complete ...</param>
</interceptor-ref>
<interceptor-ref name="workflow"/>
</interceptor-stack>
I found out that one field error can be removed by removing conversionError interceptor from the stack. But I don't think that would cause this problem. Struts should be able to show errors only defined by developer, right?
Please help me on this
You need to understand how Struts2 handles conversion errors.
Any error that occurs during type conversion may or may not wish to be reported. For example, reporting that the input "abc" could not be converted to a number might be important. On the other hand, reporting that an empty string, "", cannot be converted to a number might not be important - especially in a web environment where it is hard to distinguish between a user not entering a value vs. entering a blank value.
...
It is important to know that none of these errors are actually reported directly. Rather, they are added to a map called conversionErrors in the ActionContext. There are several ways this map can then be accessed and the errors can be reported accordingly.
There are two ways the error reporting can occur:
Globally, using the Conversion Error Interceptor
On a per-field basis, using the conversion validator
You are using both mechanisms, thus duplicating the errors found. As the documentation states, usually you don't want to report all conversion errors, and thus should remove the ConversionErrorInterceptor from the stack. Now you can selectively raise conversion errors as field errors using the conversion validator.
I found that my custom DateTimeConverter was causing the exceptions and the extra error message. Because I found the code below from Struts2 book in order to change my Date's normal format. When it throws an exception, it shows the exception on console and error message on field error rather than passing the exception to the validator. I think it is sort of bug because this class extends StrutsTypeConverter and it should work as normal converters.
public class StringToDateTimeConverter extends StrutsTypeConverter {
private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd");
public Object convertFromString(Map context, String[] strings, Class toClass) {
if (strings == null || strings.length == 0 || strings[0].trim().length() == 0) {
return null;
}
try {
Calendar calendar = Calendar.getInstance();
calendar.setTime(DATETIME_FORMAT.parse(strings[0]));
calendar.set(Calendar.HOUR, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
return calendar.getTime();
} catch (ParseException e) {
throw new TypeConversionException(e);
}
}
public String convertToString(Map context, Object date) {
if (date != null && date instanceof Date) {
return DATETIME_FORMAT.format(date);
} else {
return null;
}
}
}
Anyway I changed throw new TypeConversionException(e); to return null; and added REQUIRED validator on validation XML. Now it shows me error when I put invalid date on my date fields.
PS: Is there any other way to change Struts global date format? Thanks
I faced a similar problem yesterday and finally found a solution which I like to share. I'm using annotations in my actions for validation, so I changed default struts interceptor stack and put my SensibleConversionErrorInterceptor instead of StrutsConversionErrorInterceptor in. This one is total identically but doesn't create any validation errors. Instead they are generated by validation configured in annotations in my actions.
Here is my converter:
public class SensibleConversionErrorInterceptor extends StrutsConversionErrorInterceptor {
private static final long serialVersionUID = 8186282792289268544L;
#Override
public String intercept(ActionInvocation invocation) throws Exception {
ActionContext invocationContext = invocation.getInvocationContext();
Map<String, Object> conversionErrors = invocationContext.getConversionErrors();
ValueStack stack = invocationContext.getValueStack();
HashMap<Object, Object> fakie = null;
for (Map.Entry<String, Object> entry : conversionErrors.entrySet()) {
String propertyName = entry.getKey();
Object value = entry.getValue();
if (shouldAddError(propertyName, value)) {
// removed cause error messages are generated from annotations in actions
// String message = XWorkConverter.getConversionErrorMessage(propertyName, stack);
// Object action = invocation.getAction();
// if (action instanceof ValidationAware) {
// ValidationAware va = (ValidationAware) action;
// va.addFieldError(propertyName, message);
// }
if (fakie == null) {
fakie = new HashMap<Object, Object>();
}
fakie.put(propertyName, getOverrideExpr(invocation, value));
}
}
if (fakie != null) {
// if there were some errors, put the original (fake) values in place right before the result
stack.getContext().put(ORIGINAL_PROPERTY_OVERRIDE, fakie);
invocation.addPreResultListener(new PreResultListener() {
public void beforeResult(ActionInvocation invocation, String resultCode) {
Map<Object, Object> fakie = (Map<Object, Object>) invocation.getInvocationContext().get(ORIGINAL_PROPERTY_OVERRIDE);
if (fakie != null) {
invocation.getStack().setExprOverrides(fakie);
}
}
});
}
return invocation.invoke();
}
}
And an example action:
#Conversion
public class ProductAction extends ActionSupport {
private Product product;
// getter, setter and so on...
#Action(...)
#Validations(
requiredFields = {
#RequiredFieldValidator(
type = ValidatorType.FIELD,
fieldName = "product.validFrom",
message = "required.product.validFrom",
shortCircuit = true
)
},
conversionErrorFields = {
#ConversionErrorFieldValidator(
fieldName = "product.validFrom",
key = "invalid.fieldvalue.product.validFrom'",
shortCircuit = true
)
}
)
public String saveOrUpdate() {
// do something here...
}
}

ResponseBody cannot be resolved to a type

I'm trying to write this method in my controller:
#ResponseBody
#RequestMapping(value = {"/getTeams"}, method = RequestMethod.GET)
public void getMaxRequestSize(HttpServletResponse response) {
String autoCompleteList = null;
List<Team> teams = atService.getAllTeams();
Iterator itr = teams.iterator();
while (itr.hasNext()) {
autoCompleteList += itr.next().toString() + "\n";
}
response.setContentType("text/html");
PrintWriter writer;
try {
writer = response.getWriter();
writer.write(autoCompleteList);
} catch (IOException e) {
e.printStackTrace();
}
}
For some reason I always get an error on the ResponseBody annotation (= cannot be resolved to a type). I googled for quite a while and didn't find a solution. I'm sure it's something silly. I can use all the other annotations without any problems...
Is this a Maven project? You might be ending up with the old Spring 2.5.6 jars in your war file instead of Spring 3. Eclipse's POM editor's Dependency Hierarchy tab can help you figure out if that's the case.

Resources