Savon and Ruby to call a webservice - ruby

I have a SOAP request
<Register>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" >
<soap:Header/><soap:Body>
<Register>
<identifiers>?</identifiers>
<userId></userId>
<password pwct="?">?</password>
<locale>?</locale>
</Register>
Fully Formed Request that works is
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Header/>
<soap:Body>
<Register>
<identifiers>
<DIdentifiers>
<Section name="IDENTIFIERS">
<Property name="Type" value="Support" />
<Property name="Version" value="3.80 Jan 03 2013" />
<Property name="Timestamp" value="2015/06/08 11:57:48 GMT" />
<Property name="SerialNumber" value="00004" />
<Property name="Hostname" value="gmail.com" />
<Property name="ProductNumber" value="C00004" />
</Section><Section name="INTERFACE_IDENTIFIERS">
<Property name="InterfaceAddress" value="45.34.54.6"/>
<Property name="InterfaceMacAddress" value="00579FGHslkf"/>
</Section>
</DIdentifiers>
</identifiers>
<userId> <userId>
<password pwct="TRUE">shirl123</reg:password>
<locale>en_US</locale>
<Register>
</soap:Body>
</soap:Envelope>
I have added
WSDL client = Savon::client(wsdl: " ")
And calling method
client.call(:Register, message: { })
How can I send the above parameters in Ruby? Could not figure out the correct way to pass parameters
I'm passing
DeviceIdentifiers: {
:Section=> {
{ :Property => nil,
:attributes! => { :Property => { "name" => Type, "value" =>Support} }
}
}
But this doesnot work..
I referred to this
http://savonrb.com/version1/executing-soap-requests.html

Related

Jpos remote host query forwarding message to wrong destination

I am using channel adapter along with the Mux for routing message from client to destination server. However when it reaches to query remote host (using mux and channel adapter) it is routing back to the client rather than to the destination server as set in config files.
Please find the class used:
#Component
public class MyRequestListner implements ISORequestListener,Configurable {
private static final Logger logger = LoggerFactory.getLogger(MyRequestListner.class);
protected String queueName;
protected Space sp;
protected String destinationMux;
public static final String REQUEST = "REQUEST";
public static final String ISOSOURCE = "ISOSOURCE";
#Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {
queueName = cfg.get("queue");
destinationMux = cfg.get("destination-mux");
sp = SpaceFactory.getSpace(cfg.get("space"));
}
public boolean process (ISOSource source, ISOMsg m) {
try{
logger.info("INSIDE MY REQUEST LISTNER PROCESS : " + queueName + " : "+ destinationMux);
Context ctx = new Context ();
ctx.put (REQUEST, m);
ctx.put (ISOSOURCE, source);
sp.out (queueName, ctx);
}catch(Exception ex){
System.out.println("MY REQUEST LISTNER ERROR : "+ex);
}
return true;
}
}
package np.com.fonepay.isoswitch.iso;
import java.io.Serializable;
import org.jpos.iso.ISOMsg;
import org.jpos.iso.ISOSource;
import org.jpos.iso.ISOUtil;
import org.jpos.iso.MUX;
import org.jpos.q2.iso.QMUX;
import org.jpos.transaction.Context;
import org.jpos.transaction.TransactionParticipant;
import org.jpos.util.NameRegistrar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class QueryRemoteHost implements TransactionParticipant {
private static final Logger logger = LoggerFactory.getLogger(QueryRemoteHost.class);
#Override
public int prepare(long id, Serializable context) {
logger.info("INSIDE QUERYREMOTEHOST PREPARE");
Context ctx = (Context) context;
try {
ISOSource source = (ISOSource) ctx.get("ISOSOURCE");
ISOMsg queryRemote = (ISOMsg) ctx.get("REQUEST");
// -- forward msg to destination host
MUX remoteMux = (QMUX) NameRegistrar.getIfExists("mux.visamux");
System.out.println("===Outgoing Message To Remote Server: ");
System.out.println(ISOUtil.hexdump(queryRemote.pack()));
queryRemote = remoteMux.request(queryRemote, 30000);
System.out.println("===Incoming Message From Remote Server: ");
logger.info("Incoming Message From Remote Server: " + queryRemote);
if (queryRemote == null) {
ctx.put("CHKCARDRESP", "911");
} else {
// Modify the response message to client (if required)
ctx.put("PROCESSRESPONSE", queryRemote);
return PREPARED;
}
queryRemote.setResponseMTI();
source.send(queryRemote);
return PREPARED | NO_JOIN | READONLY;
} catch (Exception ex) {
logger.error("Query Remote Host | error | " + ex);
ex.printStackTrace();
}
return 0;
}
#Override
public void abort(long id, Serializable context) {
logger.info("INSIDE QUERYREMOTEHOST ABORT");
Context ctx = (Context) context;
try {
ISOSource source = (ISOSource) ctx.get("ISOSOURCE");
ISOMsg responseabort = (ISOMsg) ctx.get("REQUEST");
responseabort.setResponseMTI();
responseabort.set(38, "MWS000");
responseabort.set(39, "99");
source.send(responseabort);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Here are the required config files:
10_server_config.xml
<server name="MARIA_SERVER_12201" class="org.jpos.q2.iso.QServer" logger="Q2" >
<attr name="port" type="java.lang.Integer">12201</attr>
<attr name="minSessions" type="java.lang.Integer">20</attr>
<attr name="maxSessions" type="java.lang.Integer">250</attr>
<channel class="org.jpos.iso.channel.ASCIIChannel"
logger="Q2" packager="org.jpos.iso.packager.GenericPackager">
<property name="packager-config" value="cfg/packager_ebl.xml" />
</channel>
<request-listener class="np.com.fonepay.isoswitch.iso.MyRequestListner" logger="Q2">
<property name="space" value="tspace:default" />
<property name="queue" value="MyVisaTxnQueue" />
<property name="destination-mux" value="mux.visamux" />
<property name="timeout" value="30000"/>
</request-listener>
<in>VISA_IN</in>
<out>VISA_OUT</out>
</server>
15_channeladapter.xml
<channel-adaptor name='visaca' class="org.jpos.q2.iso.ChannelAdaptor" logger="Q2">
<channel class="org.jpos.iso.channel.ASCIIChannel" logger="Q2" packager="org.jpos.iso.packager.GenericPackager">
<property name="host" value="localhost" />
<property name="port" value="13301" />
<property name="packager-config" value="cfg/packager_ebl.xml" />
<property name="timeout" value="300000" />
<property name="keep-alive" value="true" />
</channel>
<in>VISA_IN</in>
<out>VISA_OUT</out>
<reconnect-delay>10000</reconnect-delay>
</channel-adaptor>
18_qserver_mux.xml
<?xml version="1.0" ?>
<mux class="np.com.fonepay.isoswitch.iso.EblMux" logger="Q2" name="visamux">
<in>VISA_OUT</in>
<out>VISA_IN</out>
<ready>visaca.ready</ready>
<key>41,11</key>
<unhandled>visaca-unhandled</unhandled>
</mux>
20_txnmanager_config.xml
<?xml version="1.0" ?>
<txnmgr name="txnmgrvisa" logger="Q2" class="org.jpos.transaction.TransactionManager">
<property name="space" value="tspace:default" />
<property name="queue" value="MyVisaTxnQueue" />
<property name="sessions" value="5" />
<participant class="np.com.fonepay.isoswitch.iso.QueryRemoteHost" logger="Q2" realm="visatxn"/>
</txnmgr>
We can see from System Output that the first it reaches to MyRequestListner and then RemoteHostQuery (Process) and instantly forward/reply message to client IP and Port whereas it has to forward it to remote host as defined in channel adapter. Since it is not able to get response(obvious) ABORT method is invoked:
<log realm="channel/127.0.0.1:64352" at="2019-09-12T12:10:40.690" lifespan="190293ms">
<receive>
<isomsg direction="incoming">
<!-- org.jpos.iso.packager.GenericPackager[cfg/packager_ebl.xml] -->
<field id="0" value="1804"/>
<field id="11" value="000000000001"/>
<field id="12" value="20190912121040"/>
<field id="24" value="831"/>
<field id="32" value="627765"/>
<field id="41" value="00627765"/>
<field id="59" value="EBLECHO"/>
<field id="93" value="627765"/>
<field id="94" value="627766DC"/>
<field id="123" value="DC"/>
</isomsg>
</receive>
</log>
2019-09-12 12:10:40.693 INFO 30136 --- [hread-2-running] n.c.f.isoswitch.iso.MyRequestListner : INSIDE MY REQUEST LISTNER PROCESS : MyVisaTxnQueue : mux.visamux
2019-09-12 12:10:40.694 INFO 30136 --- [12T12:10:40.694] n.c.f.isoswitch.iso.QueryRemoteHost : INSIDE QUERYREMOTEHOST PREPARE
===Outgoing Message To Remote Server:
<log realm="channel/127.0.0.1:64352" at="2019-09-12T12:10:40.702" lifespan="5ms">
<send>
<isomsg direction="outgoing">
<!-- org.jpos.iso.packager.GenericPackager[cfg/packager_ebl.xml] -->
<field id="0" value="1804"/>
<field id="11" value="000000000001"/>
<field id="12" value="20190912121040"/>
<field id="24" value="831"/>
<field id="32" value="627765"/>
<field id="41" value="00627765"/>
<field id="59" value="EBLECHO"/>
<field id="93" value="627765"/>
<field id="94" value="627766DC"/>
<field id="123" value="DC"/>
</isomsg>
</send>
</log>
===Incoming Message From Remote Server: java.lang.NullPointerException
at np.com.fonepay.isoswitch.iso.QueryRemoteHost.prepare(QueryRemoteHost.java:54)
at org.jpos.transaction.TransactionManager.prepare(TransactionManager.java:549)
at org.jpos.transaction.TransactionManager.prepare(TransactionManager.java:615)
at org.jpos.transaction.TransactionManager.run(TransactionManager.java:291)
at java.lang.Thread.run(Thread.java:748)
2019-09-12 12:11:10.708 INFO 30136 --- [12T12:10:40.694] n.c.f.isoswitch.iso.QueryRemoteHost : Incoming Message From Remote Server: null
2019-09-12 12:11:10.709 ERROR 30136 --- [12T12:10:40.694] n.c.f.isoswitch.iso.QueryRemoteHost : Query Remote Host | error | java.lang.NullPointerException
2019-09-12 12:11:10.711 INFO 30136 --- [12T12:11:10.710] n.c.f.isoswitch.iso.QueryRemoteHost : INSIDE QUERYREMOTEHOST ABORT
<log realm="channel/127.0.0.1:64352" at="2019-09-12T12:11:10.711">
<send>
<isomsg direction="outgoing">
<!-- org.jpos.iso.packager.GenericPackager[cfg/packager_ebl.xml] -->
<field id="0" value="1814"/>
<field id="11" value="000000000001"/>
<field id="12" value="20190912121040"/>
<field id="24" value="831"/>
<field id="32" value="627765"/>
<field id="38" value="MWS000"/>
<field id="39" value="99"/>
<field id="41" value="00627765"/>
<field id="59" value="EBLECHO"/>
<field id="93" value="627765"/>
<field id="94" value="627766DC"/>
<field id="123" value="DC"/>
</isomsg>
</send>
</log>
<log realm="org.jpos.transaction.TransactionManager" at="2019-09-12T12:11:10.715" lifespan="30021ms">
<abort>
txnmgrvisa-0:idle:2
<context>
REQUEST:
<isomsg direction="outgoing">
<!-- org.jpos.iso.packager.GenericPackager[cfg/packager_ebl.xml] -->
<field id="0" value="1814"/>
<field id="11" value="000000000001"/>
<field id="12" value="20190912121040"/>
<field id="24" value="831"/>
<field id="32" value="627765"/>
<field id="38" value="MWS000"/>
<field id="39" value="99"/>
<field id="41" value="00627765"/>
<field id="59" value="EBLECHO"/>
<field id="93" value="627765"/>
<field id="94" value="627766DC"/>
<field id="123" value="DC"/>
</isomsg>
ISOSOURCE: org.jpos.iso.channel.ASCIIChannel#4533bed0
CHKCARDRESP: 911
</context>
prepare: np.com.fonepay.isoswitch.iso.QueryRemoteHost ABORTED
abort: np.com.fonepay.isoswitch.iso.QueryRemoteHost
in-transit=0, head=3, tail=3, paused=0, outstanding=0, active-sessions=5/5, tps=0, peak=1, avg=0.00, elapsed=30020ms
<profiler>
prepare: np.com.fonepay.isoswitch.iso.QueryRemoteHost [30015.2/30015.2]
abort: np.com.fonepay.isoswitch.iso.QueryRemoteHost [4.7/30020.0]
end [0.9/30021.0]
</profiler>
</abort>
</log>
Your problem is here:
<server name="MARIA_SERVER_12201" class="org.jpos.q2.iso.QServer" logger="Q2" >
<attr name="port" type="java.lang.Integer">12201</attr>
<attr name="minSessions" type="java.lang.Integer">20</attr>
<attr name="maxSessions" type="java.lang.Integer">250</attr>
<channel class="org.jpos.iso.channel.ASCIIChannel"
logger="Q2" packager="org.jpos.iso.packager.GenericPackager">
<property name="packager-config" value="cfg/packager_ebl.xml" />
</channel>
<request-listener class="np.com.fonepay.isoswitch.iso.MyRequestListner" logger="Q2">
<property name="space" value="tspace:default" />
<property name="queue" value="MyVisaTxnQueue" />
<property name="destination-mux" value="mux.visamux" />
<property name="timeout" value="30000"/>
</request-listener>
<!--you need to delete the following two lines -->
<in>VISA_IN</in> <!--here-->
<out>VISA_OUT</out> <!-- and here-->
</server>
As mentioned in the comments:
unless your server (the one receiving requests from your client, for which you didn't share the deploy file) has an in queue named "VISA_IN"
You have the in and out queues defined in the server, with the same names of the channel, so the server is competing with the channel for the messages put in those queues, messages that goes to the in queue of the server are sent to it's client, you need to remove them, since you are not using the queues to communicate to the client connected to the server but calling send() from a transaction participant

Error occurred in the mediation of the class mediator WSO2 ESB

I used wso2 esb 5.0 for create proxy services. I created proxy service using class mediator. Below is the java class.
public class CalculatePaymentAmount extends AbstractMediator {
public boolean mediate(MessageContext messageContext) {
String noOfMonths = messageContext.getEnvelope().getBody().getFirstElement().
getFirstChildWithName(new QName("noOfMonths")).getText();
String InsuranceRate = messageContext.getEnvelope().getBody().getFirstElement().
getFirstChildWithName(new QName("InsuranceRate")).getText();
DecimalFormat decimalFormat = new DecimalFormat("#.##");
double totalAmount = Double.parseDouble(noOfMonths) * Double.parseDouble(InsuranceRate);
messageContext.setProperty("noOfMonths", noOfMonths);
messageContext.setProperty("paymentAmount", decimalFormat.format(totalAmount));
return true;
}
public String getType() {
return null;
}
public void setTraceState(int traceState) {
traceState = 0;
}
public int getTraceState() {
return 0;
}
}
I created the proxy service using class mediator. Below define the proxy code.
<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
name="PaymentAmountProxy"
startOnLoad="true"
statistics="disable"
trace="disable"
transports="http,https">
<target>
<inSequence>
<log/>
<class name="com.mediator.java.CalculatePaymentAmount"/>
<property expression="get-property('default','noOfMonths')"
name="getNoOfMonths"
scope="default"
type="STRING"/>
<property expression="get-property('default','paymentAmount')"
name="getPaymentAmount"
scope="default"
type="STRING"/>
<log>
<property expression="get-property('default','getNoOfMonths')"
name="No.Of Months:"/>
<property expression="get-property('default','getPaymentAmount')"
name="Paymrent Amount:"/>
</log>
</inSequence>
</target>
<description/>
</proxy>
This one working fine and gave expected response.
But when I use this class mediator with other mediators, it getting errors when I invoked the proxy service.
Below mentioned the proxy service that I used with class mediator.
<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
name="LatestLicenseRenewalSystem"
startOnLoad="true"
statistics="disable"
trace="disable"
transports="http,https">
<target>
<inSequence>
<log/>
<property expression="get-property('transport','VehicleNo')"
name="vehicleNo"
scope="default"
type="STRING"/>
<log>
<property expression="get-property('default','vehicleNo')" name="VehicleNo"/>
</log>
<payloadFactory media-type="xml">
<format>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsa="http://www.w3.org/2005/08/addressing"
xmlns:sam="http://sample.esb.org">
<soapenv:Header/>
<soapenv:Body>
<sam:getPolicyID>
<sam:vehicleNumber>$1</sam:vehicleNumber>
</sam:getPolicyID>
</soapenv:Body>
</soapenv:Envelope>
</format>
<args>
<arg evaluator="xml" expression="get-property('default','vehicleNo')"/>
</args>
</payloadFactory>
<log level="full"/>
<header name="Action" scope="default" value="urn:getCertificateID"/>
<call>
<endpoint>
<address format="soap12"
uri="http://172.17.0.1:9763/services/EmissionTestService.EmissionTestServiceHttpSoap12Endpoint/">
<enableAddressing/>
</address>
</endpoint>
</call>
<log level="full"/>
<property xmlns:ns="http://sample.esb.org"
expression="//ns:getCertificateIDResponse/ns:return"
name="certificateID"
scope="default"
type="STRING"/>
<log>
<property expression="get-property('default','certificateID')"
name="CertificateID"/>
</log>
<class name="com.mediator.java.CalculatePaymentAmount"/>
<property expression="get-property('default','noOfMonths')"
name="getNoOfMonths"
scope="default"
type="STRING"/>
<property expression="get-property('default','paymentAmount')"
name="getPaymentAmount"
scope="default"
type="STRING"/>
<log>
<property expression="get-property('default','getNoOfMonths')"
name="No.Of Months:"/>
<property expression="get-property('default','getPaymentAmount')"
name="Paymrent Amount:"/>
</log>
<respond/>
</inSequence>
</target>
<description/>
</proxy>
Below error is occurred when invoking the above proxy service.
LogMediator To: http://www.w3.org/2005/08/addressing/anonymous, WSAction: urn:getCertificateIDResponse, SOAPAction: urn:getCertificateIDResponse, ReplyTo: http://www.w3.org/2005/08/addressing/anonymous, MessageID: urn:uuid:6f4557eb-b8ff-4c19-bbe8-4c7e929d8386, Direction: request, MESSAGE = Executing default 'fault' sequence, ERROR_CODE = 0, ERROR_MESSAGE = Error occured in the mediation of the class mediator, Envelope: <?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"><soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing"><wsa:Action>urn:getCertificateIDResponse</wsa:Action><wsa:RelatesTo>urn:uuid:6f4557eb-b8ff-4c19-bbe8-4c7e929d8386</wsa:RelatesTo></soapenv:Header><soapenv:Body><ns:getCertificateIDResponse xmlns:ns="http://sample.esb.org"><ns:return>-1250719063</ns:return></ns:getCertificateIDResponse></soapenv:Body></soapenv:Envelope>
Can anyone help me to solve this. Any help or workarounds are really appreciated.
Your class mediator is accessing the elements 'noOfMonths' and 'InsuranceRate' from the message context. But according to the error log, the message context is having a different soap envelope which does not have the above elements.
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"><soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing"><wsa:Action>urn:getCertificateIDResponse</wsa:Action><wsa:RelatesTo>urn:uuid:6f4557eb-b8ff-4c19-bbe8-4c7e929d8386</wsa:RelatesTo></soapenv:Header><soapenv:Body><ns:getCertificateIDResponse xmlns:ns="http://sample.esb.org"><ns:return>-1250719063</ns:return></ns:getCertificateIDResponse></soapenv:Body></soapenv:Envelope>
This must me the response received from the call operation before the class mediator.
You have to either isolate the class mediator from the call operation and use different proxy services or move the class mediator above the payload factory.
For solve this issue, use payloadFactory mediator before the class mediator and set the parameters for the payload. I mentioned below the code.
<payloadFactory media-type="xml">
<format>
<paymentDetails xmlns="">
<noOfMonths>$1</noOfMonths>
<InsuranceRate>$2</InsuranceRate>
</paymentDetails>
</format>
<args>
<arg evaluator="xml" expression="get-property('default','noOfMonths')"/>
<arg evaluator="xml" expression="get-property('default','InsuranceRate')"/>
</args>
</payloadFactory>
<class name="com.mediator.java.CalculatePaymentAmount"/>

Passing a bearer token in a 'Web Test' without Visual Studio?

I want to import a ".webtest" in Azure's Application Insights availability feature. I dont have a test edition of Visual Studio, but this MSDN article suggests using Fiddler as another option to creating web tests.
I need to perform 2 requests on a REST API:
Request a bearer token from the connect/token endpoint.
Perform a GET at api/resources with the bearer token (retrieved from the above request) in the header.
It's a typical client credentials OAuth 2 flow.
I cannot seem to figure out how to do this with Fiddler. Basically I need to extract a value from the response body of request 1 and use it as the header value in request 2.
This is what the web test looks like without passing the token:
<?xml version="1.0" encoding="utf-8"?>
<TestCase Name="FiddlerGeneratedWebTest" Id="" Owner="" Description="" Priority="0" Enabled="True" CssProjectStructure="" CssIteration="" DeploymentItemsEditable="" CredentialUserName="" CredentialPassword="" PreAuthenticate="True" Proxy="" RequestCallbackClass="" TestCaseCallbackClass="">
<Items>
<Request Method="POST" Version="1.1" Url="https://example.com/connect/token" ThinkTime="8" Timeout="60" ParseDependentRequests="True" FollowRedirects="True" RecordResult="True" Cache="False" ResponseTimeGoal="0" Encoding="utf-8">
<Headers>
<Header Name="Content-Type" Value="application/x-www-form-urlencoded" />
</Headers>
<FormPostHttpBody ContentType="application/x-www-form-urlencoded">
<FormPostParameter Name="client_id" Value="myclientid" UrlEncode="True" />
<FormPostParameter Name="client_secret" Value="password123" UrlEncode="True" />
<FormPostParameter Name="grant_type" Value="client_credentials" UrlEncode="True" />
<FormPostParameter Name="scope" Value="myscopes" UrlEncode="True" />
</FormPostHttpBody>
</Request>
<Request Method="GET" Version="1.1" Url="https://example.com/api/resources" ThinkTime="0" Timeout="60" ParseDependentRequests="True" FollowRedirects="True" RecordResult="True" Cache="False" ResponseTimeGoal="0" Encoding="utf-8">
<Headers>
<Header Name="Authorization" Value="Bearer {{token}}" />
</Headers>
</Request>
</Items>
</TestCase>
Assuming this comes back as the following example you can use a regex extraction to get it.
{"token_type":"Bearer","scope":"user_impersonation","expires_in":"3600 ... "access_token":"{{TOKEN}}", ...}
<?xml version="1.0" encoding="utf-8"?>
<TestCase Name="FiddlerGeneratedWebTest" Id="" Owner="" Description="" Priority="0" Enabled="True" CssProjectStructure="" CssIteration="" DeploymentItemsEditable="" CredentialUserName="" CredentialPassword="" PreAuthenticate="True" Proxy="" RequestCallbackClass="" TestCaseCallbackClass="">
<Items>
<Request Method="POST" Version="1.1" Url="https://example.com/connect/token" ThinkTime="8" Timeout="60" ParseDependentRequests="True" FollowRedirects="True" RecordResult="True" Cache="False" ResponseTimeGoal="0" Encoding="utf-8">
<ExtractionRules>
<ExtractionRule Classname="Microsoft.VisualStudio.TestTools.WebTesting.Rules.ExtractRegularExpression, Microsoft.VisualStudio.QualityTools.WebTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" VariableName="token" DisplayName="Extract Regular Expression" Description="Extract text from the response matching a regular expression and place it into the test context.">
<RuleParameters>
<RuleParameter Name="RegularExpression" Value=".*"access_token":"([^"]*)".*" />
<RuleParameter Name="IgnoreCase" Value="True" />
<RuleParameter Name="Required" Value="True" />
<RuleParameter Name="Index" Value="0" />
<RuleParameter Name="HtmlDecode" Value="True" />
<RuleParameter Name="UseGroups" Value="True" />
</RuleParameters>
</ExtractionRule>
</ExtractionRules>
<Headers>
<Header Name="Content-Type" Value="application/x-www-form-urlencoded" />
</Headers>
<FormPostHttpBody ContentType="application/x-www-form-urlencoded">
<FormPostParameter Name="client_id" Value="myclientid" UrlEncode="True" />
<FormPostParameter Name="client_secret" Value="password123" UrlEncode="True" />
<FormPostParameter Name="grant_type" Value="client_credentials" UrlEncode="True" />
<FormPostParameter Name="scope" Value="myscopes" UrlEncode="True" />
</FormPostHttpBody>
</Request>
<Request Method="GET" Version="1.1" Url="https://example.com/api/resources" ThinkTime="0" Timeout="60" ParseDependentRequests="True" FollowRedirects="True" RecordResult="True" Cache="False" ResponseTimeGoal="0" Encoding="utf-8">
<Headers>
<Header Name="Authorization" Value="Bearer {{token}}" />
</Headers>
</Request>
</Items>
</TestCase>
To compliment James Davis's answer, if you need to login to https://yourapp.com/auth/login by posting the JSON:
{
user: 'youruser',
password: 'yourpassword'
}
first base64 encode the json:
> echo "{user: 'youruser', password: 'yourpassword'}" | base64
e3VzZXI6ICd5b3VydXNlcicsIHBhc3N3b3JkOiAneW91cnBhc3N3b3JkJ30K
Then pass this base64 value in a StringHttpBody tag
<?xml version="1.0" encoding="utf-8"?>
<WebTest Name="login-healthcheck" Id="e91b6e1d-3fa0-475f-a18b-b694b463589c" Owner="" Priority="0" Enabled="True" CssProjectStructure="" CssIteration="" Timeout="0" WorkItemIds="" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010" Description="" CredentialUserName="" CredentialPassword="" PreAuthenticate="True" Proxy="default" StopOnError="False" RecordedResultFile="" ResultsLocale="">
<Items>
<Request Method="POST" Guid="ef9d1d00-5663-476a-a3cb-ccf49c4d2229" Version="1.1" Url="https://yourapp.com/auth/login" ThinkTime="8" Timeout="60" ParseDependentRequests="True" FollowRedirects="True" RecordResult="True" Cache="False" ResponseTimeGoal="0" Encoding="utf-8" ExpectedHttpStatusCode="0" ExpectedResponseUrl="" ReportingName="" IgnoreHttpStatusCode="False">
<Headers>
<Header Name="Content-Type" Value="application/json" />
</Headers>
<ExtractionRules>
<ExtractionRule Classname="Microsoft.VisualStudio.TestTools.WebTesting.Rules.ExtractRegularExpression, Microsoft.VisualStudio.QualityTools.WebTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" VariableName="token" DisplayName="Extract Regular Expression" Description="Extract text from the response matching a regular expression and place it into the test context.">
<RuleParameters>
<RuleParameter Name="RegularExpression" Value=".*"access_token":"([^"]*)".*" />
<RuleParameter Name="IgnoreCase" Value="True" />
<RuleParameter Name="Required" Value="True" />
<RuleParameter Name="Index" Value="0" />
<RuleParameter Name="HtmlDecode" Value="True" />
<RuleParameter Name="UseGroups" Value="True" />
</RuleParameters>
</ExtractionRule>
</ExtractionRules>
<StringHttpBody ContentType="application/json" InsertByteOrderMark="False">e3VzZXI6ICd5b3VydXNlcicsIHBhc3N3b3JkOiAneW91cnBhc3N3b3JkJ30K</StringHttpBody>
</Request>
<Request Method="GET" Guid="d566422f-af74-47bf-90aa-0c66db6ef567" Version="1.1" Url="https://yourapp.com/api/v1/healthcheck" ThinkTime="0" Timeout="60" ParseDependentRequests="True" FollowRedirects="True" RecordResult="True" Cache="False" ResponseTimeGoal="0" Encoding="utf-8" ExpectedHttpStatusCode="0" ExpectedResponseUrl="" ReportingName="" IgnoreHttpStatusCode="False">
<Headers>
<Header Name="Authorization" Value="Bearer {{token}}" />
</Headers>
</Request>
</Items>
</WebTest>
Worked for me on Azure Application Insights Availability checking

Spring Batch Integration with BeanIO

I am trying to integrate BeanIO with spring batch. Using BeanIO I am reading a fixed length stream file. I have tested and verified the code to read the flat file using a standalone class and it works seamlessly but when I tried to integrate it with Spring Batch the doRead() method of BeanIOFlatFileItemReader is not getting invoked and some how directly the RedemptionEventCustomProcessor written by me is getting invoked.
I get below stacktrace on console.:
:::::::::::::In Processor::::::::::::::::
Exit Status : FAILED job Id 0 [java.lang.NullPointerException]
Done
Please find the dependencies used below:
spring-batch-core version 2.1.9.RELEASE
spring-batch-infrastructure 2.1.9.RELEASE
beanio-2.1.0.M2
Please find below the resources files as mentioned below:
earn-api-batch - spring batch xml file
earn-api-batch-context - spring batch context xml file
earn-api-mapping.xml - BeanIO mapping xml file
earn-api-batch.xml:
<import resource="classpath:earn-api-batch-context.xml" />
<batch:job id="processEventJob">
<batch:step id="step">
<batch:tasklet>
<batch:chunk reader="RedemptionFileReader" writer="RedemptionEventCustomWriter" processor="RedemptionEventCustomProcessor" commit-interval="1"></batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
<bean id="RedemptionFileReader" class="org.beanio.spring.BeanIOFlatFileItemReader">
<property name="streamMapping" value="classpath:/earn-api-mapping.xml" />
<property name="streamName" value="redemptionFile" />
<property name="resource" value="classpath:/RedemptionTest" />
</bean>
<bean id="RedemptionEventCustomWriter" class="org.beanio.spring.BeanIOFlatFileItemWriter">
<property name="streamMapping" value="classpath:/earn-api-mapping.xml" />
<property name="streamName" value="redemptionFile" />
<property name="resource" value="file:Redemption.txt" />
</bean>
earn-api-batch-context:
<context:component-scan base-package="com.aexp.earn.api.batch" />
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
<bean id="jobRepository" class="org.springframework.batch.core.repository.support.SimpleJobRepository">
<constructor-arg>
<bean class="org.springframework.batch.core.repository.dao.MapJobInstanceDao"/>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.batch.core.repository.dao.MapJobExecutionDao" />
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.batch.core.repository.dao.MapStepExecutionDao"/>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.batch.core.repository.dao.MapExecutionContextDao"/>
</constructor-arg>
</bean>
<bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
earn-api-mapping.xml:
<beanio xmlns="http://www.beanio.org/2012/03"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.beanio.org/2012/03 http://www.beanio.org /2012/03/mapping.xsd">
<stream name="redemptionFile" format="fixedlength">
<!-- 'class' binds the header record to a java.util.HashMap -->
<record name="header" class="map">
<!-- 'rid' indicates this field is used to identify the record -->
<field name="recordType" length="1" rid="true" literal="1" />
<field name="fileSeqNo" length="10" padding=" " justify="right" />
<!-- 'format' can be used to provide Date and Number formats -->
<field name="fileDate" length="8" type="date" format="yyyyMMdd" />
</record>
<record name="nar" class="com.aexp.earn.api.batch.vo.NonAirline">
<field name="recordType" length="1" ignore="true" />
<field name="redeemType" rid="true" literal="1" length="1" />
<field name="mmNo" length="10" />
<field name="cmNo" length="19" padding=" " justify="right" />
<field name="rewardProgCode" length="2" padding="0" justify="right" />
<field name="certNo" length="10"/>
<field name="certFaceValue" length="13" padding=" " justify="right" />
<field name="redeemTimeStamp" length="26" />
<field name="statusCode" length="2" />
<field name="statusTimeStamp" length="26" />
<field name="rewardCode" length="4" />
<field name="rewardSubCode" length="4" />
<field name="transId" length="15" />
<field name="transSrcId" length="3" />
<field name="narRequestId" length="14" />
<field name="narRequestLnId" length="5" padding=" " justify="right" />
</record>
<record name="ar" class="com.aexp.earn.api.batch.vo.Airline">
<field name="recordType" length="1" ignore="true" />
<field name="redeemType" rid="true" literal="2" length="1" />
<field name="partnerTransferType" length="1" />
<field name="mmNo" length="10" />
<field name="cmNo" length="19" padding=" " justify="right" />
<field name="rewardProgCode" length="2" />
<field name="frequentFlyerNo" length="15" />
<field name="partnerCode" length="2"/>
<field name="redeemTimeStamp" length="26" />
<field name="transferedMiles" length="12" padding=" " justify="right" />
<field name="transferedStatus" length="1" />
<field name="statusTimeStamp" length="26" />
<field name="originSourceCode" length="3" />
<field name="transId" length="15" />
<field name="transSrcId" length="3" />
<field name="arRequestId" length="14" />
<field name="arRequestLnId" length="5" padding=" " justify="right" />
</record>
<record name="pwp" class="com.aexp.earn.api.batch.vo.PWP">
<field name="recordType" length="1" ignore="true" />
<field name="redeemType" rid="true" literal="3" length="1" />
<field name="mmNo" length="10" />
<field name="cmNo" length="19" padding=" " justify="right" />
<field name="rewardProgCode" length="2" />
<field name="redeemTimeStamp" length="26" />
<field name="adjustMiles" length="12" />
<field name="adjustCD" length="4" />
<field name="transId" length="15" />
<field name="transSrcId" length="3" />
<field name="pwpRequestId" length="14" padding=" " justify="right" />
<field name="pwpRequestLnId" length="5" />
</record>
<record name="pap" class="com.aexp.earn.api.batch.vo.PAP">
<field name="recordType" length="1" ignore="true" />
<field name="redeemType" rid="true" literal="4" length="1" />
<field name="mmNo" length="10" />
<field name="cmNo" length="19" padding=" " justify="right" />
<field name="rewardProgCode" length="2" />
<field name="redeemTimeStamp" length="26" />
<field name="chnlPtnrId" length="5" />
<field name="orderId" length="32" />
<field name="partnerReserId" length="32" />
<field name="confirmId" length="16" padding=" " justify="right" />
<field name="actPtCnt" length="12" />
<field name="rewdActTypeCd" length="3" />
<field name="seqNo" length="5" />
</record>
<!-- 'target' binds the trailer record to the Integer record count field -->
<record name="trailer" target="recordCount">
<!-- 'literal' is used to define constant values -->
<field name="recordType" rid="true" literal="9" length="2" />
<!-- 'type' can be declared where bean introspection is not possible -->
<field name="recordCount" length="7" type="int" />
</record>
</stream>
</beanio>
Find below the classes:
Main class:
public class EventStart {
#SuppressWarnings("resource")
public static void main(String[] args) {
String[] springConfig =
{
"earn-api-batch.xml"
};
ClassPathXmlApplicationContext appContext = null;
appContext = new ClassPathXmlApplicationContext(springConfig);
JobLauncher jobLauncher = (JobLauncher) appContext.getBean("jobLauncher");
Job job = (Job) appContext.getBean("processEventJob");
try {
JobExecution execution = jobLauncher.run(job, new JobParameters());
System.out.println("Exit Status : " + execution.getStatus() + " job Id " + execution.getJobId() + " " + execution.getAllFailureExceptions());
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Done");
}
}
Reader Class:
#Component(value = "RedemptionFileReader")
#Scope("step")
public class RedemptionFileReader extends BeanIOFlatFileItemReader<Map<String,Object>>{
/* #Value("#{batchProps['redemption.server.file.path']}")
private Resource resource;*/
Map<String,Object> map = new HashMap<String,Object>();
/*#Override
public String read() throws Exception, UnexpectedInputException,
ParseException, NonTransientResourceException {
System.out.println("hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh");
// TODO Auto-generated method stub
return null;
}*/
#Override
public Map<String,Object> doRead() throws Exception, UnexpectedInputException,
ParseException, NonTransientResourceException {
System.out.println("HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH");
/*if (null != recordsCount) {
return null;
}*/
map = identifyRecord();
return map;
}
private Map<String,Object> identifyRecord() throws FileNotFoundException {
Map<String,Object> map=new HashMap<String,Object>();
ArrayList<NonAirline> narList = new ArrayList<NonAirline>();
ArrayList<Airline> arList = new ArrayList<Airline>();
ArrayList<PWP> pwpList = new ArrayList<PWP>();
ArrayList<PAP> papList = new ArrayList<PAP>();
// create a StreamFactory
StreamFactory factory = StreamFactory.newInstance();
// load the mapping file
factory.load("src/main/resources/earn-api-mapping.xml");
// use a StreamFactory to create a BeanReader
BeanReader in = factory.createReader("redemptionFile", new File("src/main/resources/RedemptionTest"));
Object record = null;
String recordCount = "";
while ((record = in.read()) != null) {
if ("header".equals(in.getRecordName())) {
//Map<String,Object> header = (Map<String,Object>) record;
//System.out.println(header.get("fileDate"));
System.out.println("Header");
}
else if ("nar".equals(in.getRecordName())) {
NonAirline nar = (NonAirline) record;
System.out.println("NAR Redeem Type: " + nar.getRedeemType());
System.out.println("NAR MM No.: " + nar.getMmNo());
System.out.println("NAR CM No.: " + nar.getCmNo());
narList.add(nar);
}
else if ("ar".equals(in.getRecordName())) {
Airline ar = (Airline) record;
System.out.println("AR Redeem Type: " + ar.getRedeemType());
System.out.println("AR MM No.: " + ar.getMmNo());
System.out.println("AR CM No.: " + ar.getCmNo());
arList.add(ar);
}
else if ("pwp".equals(in.getRecordName())) {
PWP pwp = (PWP) record;
System.out.println("PWP Redeem Type: " + pwp.getRedeemType());
System.out.println("PWP MM No.: " + pwp.getMmNo());
System.out.println("PWP CM No.: " + pwp.getCmNo());
pwpList.add(pwp);
}
else if ("pap".equals(in.getRecordName())) {
PAP pap = (PAP) record;
System.out.println("PAP Redeem Type: " + pap.getRedeemType());
System.out.println("PAP MM No.: " + pap.getMmNo());
System.out.println("PAP CM No.: " + pap.getCmNo());
papList.add(pap);
}
else if ("trailer".equals(in.getRecordName())) {
recordCount = (String) record;
System.out.println("Trailer");
System.out.println(recordCount + " contacts processed");
}
}
map.put("NAR", narList);
map.put("AR", arList);
map.put("PWP", pwpList);
map.put("PAP", papList);
in.close();
return map;
}
}
Processor Class:
#Component(value = "RedemptionEventCustomProcessor")
public class RedemptionEventCustomProcessor implements ItemProcessor<Map<String,Object>, Map<String,Object>> {
ArrayList<NonAirline> narList = new ArrayList<NonAirline>();
ArrayList<Airline> arList = new ArrayList<Airline>();
ArrayList<PWP> pwpList = new ArrayList<PWP>();
ArrayList<PAP> papList = new ArrayList<PAP>();
#SuppressWarnings("unchecked")
#Override
public Map<String,Object> process(Map<String,Object> map) throws Exception {
System.out.println(":::::::::::::In Processor::::::::::::::::");
narList = (ArrayList<NonAirline>) map.get("NAR");
for(int i=0; i <= narList.size(); i++)
{
NonAirline nar = narList.get(i);
System.out.println("CM No. " + nar.getCmNo());
}
return map;
}
}
Please let me know if any other information is required. Any help will be really appreciated, please consider I am new to both BeanIO and spring as well.
If anyone could share a working example of BeanIO using Spring Batch that should also help.
Thank You in advance.
I too am using beanio with spring batch. The error you are getting is perhaps for the version issue. Here is my xsd config from job xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:batch="http://www.springframework.org/schema/batch" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

Return collection from unbound function gives serialization error

I have the following setup which should return a list of FooBars. But it fails to serialize for some reason :(
Config
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.Function("Test").ReturnsCollection<FooBar>();
config.MapODataServiceRoute(routeName: "OData",
routePrefix: "OData",
model: builder.GetEdmModel());
Unbound Function
[HttpGet]
[ODataRoute("Test")]
public IEnumerable<FooBar> Test()
{
var foobars = new List<FooBar>();
// populate list...
return foobars;
}
Error
Calling localhost/odata/Test gives the below error
{
"error": {
"code": "",
"message": "An error has occurred.",
"innererror": {
"message": "The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; odata.metadata=minimal'.",
"type": "System.InvalidOperationException",
"stacktrace": "",
"internalexception": {
"message": "The related entity set could not be found from the OData path. The related entity set is required to serialize the payload.",
"type": "System.Runtime.Serialization.SerializationException",
"stacktrace": " at System.Web.OData.Formatter.Serialization.ODataFeedSerializer.WriteObject(Object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)\r\n at System.Web.OData.Formatter.ODataMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content, HttpContentHeaders contentHeaders)\r\n at System.Web.OData.Formatter.ODataMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__1b.MoveNext()"
}
}
}
}
$Metadata snippet
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0"
xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
<Schema Namespace="MyNamespace"
xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityType Name="FooBar">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Edm.Int32" Nullable="false" />
<Property Name="A" Type="Edm.String" />
<Property Name="B" Type="Edm.Int32" Nullable="false" />
<Property Name="C" Type="Edm.Int32" Nullable="false" />
</EntityType>
<EntityType Name="FooBarD" BaseType="MyNamespace.FooBar">
<Property Name="D" Type="Edm.Int32" Nullable="false" />
</EntityType>
<EntityType Name="FooBarE" BaseType="MyNamespace.FooBar">
<Property Name="E" Type="Edm.Int32" Nullable="false" />
</EntityType>
</Schema>
<Schema Namespace="Default"
xmlns="http://docs.oasis-open.org/odata/ns/edm">
<Function Name="Test">
<ReturnType Type="Collection(MyNamespace.FooBar)" />
</Function>
<EntityContainer Name="Container">
<FunctionImport Name="Test" Function="Default.Test" IncludeInServiceDocument="true" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
Turns out that because I had this builder.EntitySet<FooBar>("BAM"); set, I needed to call ReturnsCollectionFromEntitySet instead.
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<FooBar>("BAM"); // previously omitted from example
builder.Function("Test").ReturnsCollectionFromEntitySet<FooBar>("BAM")();
config.MapODataServiceRoute(routeName: "OData",
routePrefix: "OData",
model: builder.GetEdmModel());

Resources