JavaSampler Result - jmeter

Hi Iam Fresher in Jmeter
I have wrote one Java Sampler code. I don't know that is correct or wrong. If I put that URL and parameter in Http Request getting proper result, but if written as a javasampler i didn't get that result, Iam getting Pass result but no response and request data
My Sampler code is:
package org.apache.jmeter.protocol.java.test;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.java.sampler.AbstractJavaSamplerClient;
import org.apache.jmeter.protocol.java.sampler.JavaSamplerContext;
import org.apache.jmeter.samplers.SampleResult;
public class ExampleJavaSampler extends AbstractJavaSamplerClient implements Serializable {
String mySvc = "";
JavaSamplerContext context;
public Arguments getDefaultParameters(){
Arguments arg = new Arguments();
arg.addArgument("url", "http://www.url.com:5252/Switch/Download");
arg.addArgument("e_type", "bank");
arg.addArgument("e_id", "4");
arg.addArgument("b_id", "1");
arg.addArgument("a_id", "0002");
arg.addArgument("link_branch", "");
arg.addArgument("terminal_id", "");
arg.addArgument("version", "10");
arg.addArgument("entity", "100");
System.out.println("inside default");
return arg;
}
public void setupTest(JavaSamplerContext context) {
System.out.println("inside Setup");
}
public SampleResult runTest(JavaSamplerContext context) {
System.out.println("Inside Run test:");
String urls = context.getParameter("url");
String e_type = context.getParameter("e_type");
String e_id = context.getParameter("e_id");
String b_id = context.getParameter("b_id");
String a_id = context.getParameter("a_id");
String l_branch = context.getParameter("e_type");
String t_id = context.getParameter("e_type");
String oion = context.getParameter("e_type");
String entity = context.getParameter("e");
SampleResult result = new SampleResult();
result.getURL();
result.setSampleLabel("Test Result");
result.setDataType(SampleResult.TEXT);
result.sampleStart();
try{
java.net.URL url = new java.net.URL(urls+"?=e_type="+e_type+"&e_id="+e_id+"&b_id="+b_id);
System.out.println(url);
java.net.HttpURLConnection connection = (java.net.HttpURLConnection)url.openConnection(); // have to cast connection
connection.setRequestMethod("POST");
connection.connect();
result.sampleEnd(); // stop stopwatch
result.setSuccessful( true );
result.setResponseMessage( "Successfully performed action" );
result.setResponseCodeOK(); // 200 code
} catch (Exception e) {
result.sampleEnd(); // stop stopwatch
result.setSuccessful( false );
result.setResponseMessage( "Exception: " + e );
// get stack trace as a String to return as document data
java.io.StringWriter stringWriter = new java.io.StringWriter();
e.printStackTrace( new java.io.PrintWriter( stringWriter ) );
result.setResponseData( stringWriter.toString() );
result.setDataType( org.apache.jmeter.samplers.SampleResult.TEXT );
result.setResponseCode( "500" );
}
return result;
}
void teardownTest() {
System.out.println("inside tear Down:");
}
}
After this code I made .jar file and put lib/ext. Then I called in Javarequest and all parameters are diplayed there, then I run this Test plan, getting success message nut no result
This is the right way or we have to add some thing for there for result?

I've already responded here.
You need to call result.setResponseData() inside your try block elsewise you won't see anything on success. "Response Data" piece of "View Results Tree" listener is populated only on error according to your code.

Related

Wiremock request templating in standalone mode: can I use a XML file as response template and inject value with XPATH?

I know that request template supports XPath, so that I can get value from request like {{xPath request.body '/outer/inner/text()'}}. I already have a XML file as response, and I want to inject this value I got from request, but keep the other parts of this response XML intact. For example, I want to inject it to XPATH /svc_result/slia/pos/msid.
And I need to use it in standalone mode.
I see another question(Wiremock Stand alone - How to manipulate response with request data) but that was with JSON, I have XML request/response.
How can it be done? Thanks.
For example, I have this definition of mapping:
{
"request": {
"method": "POST",
"bodyPatterns": [
{
"matchesXPath": {
"expression": "/svc_init/slir/msids/msid[#type='MSISDN']/text()",
"equalTo": "200853000105614"
}
},
{
"matchesXPath": "/svc_init/hdr/client[id and pwd]"
}
]
},
"response": {
"status": 200,
"bodyFileName": "slia.xml",
"headers": {
"Content-Type": "application/xml;charset=UTF-8"
}
}
}
And this request:
<?xml version="1.0"?>
<!DOCTYPE svc_init>
<svc_init ver="3.2.0">
<hdr ver="3.2.0">
<client>
<id>dummy</id>
<pwd>dummy</pwd>
</client>
</hdr>
<slir ver="3.2.0" res_type="SYNC">
<msids>
<msid type="MSISDN">200853000105614</msid>
</msids>
</slir>
</svc_init>
I expect this response, with xxxxxxxxxxx replaced with the <msid> in the request.
<?xml version="1.0" ?>
<!DOCTYPE svc_result SYSTEM "MLP_SVC_RESULT_320.DTD">
<svc_result ver="3.2.0">
<slia ver="3.0.0">
<pos>
<msid type="MSISDN" enc="ASC">xxxxxxxxxxx</msid>
<pd>
<time utc_off="+0800">20111122144915</time>
<shape>
<EllipticalArea srsName="www.epsg.org#4326">
<coord>
<X>00 01 01N</X>
<Y>016 31 53E</Y>
</coord>
<angle>0</angle>
<semiMajor>2091</semiMajor>
<semiMinor>2091</semiMinor>
<angularUnit>Degrees</angularUnit>
</EllipticalArea>
</shape>
<lev_conf>90</lev_conf>
</pd>
<gsm_net_param>
<cgi>
<mcc>100</mcc>
<mnc>01</mnc>
<lac>2222</lac>
<cellid>10002</cellid>
</cgi>
<neid>
<vmscid>
<vmscno>00004946000</vmscno>
</vmscid>
<vlrid>
<vlrno>99994946000</vlrno>
</vlrid>
</neid>
</gsm_net_param>
</pos>
</slia>
</svc_result>
My first thought was to use transformerParameters to change the response file by inserting the value from the body. Unfortunately, WireMock doesn't resolve the helpers before inserting them into the body response. So while we can reference that MSID value via an xpath helper like
{{xPath request.body '/svc_init/slir/msids/msid/text()'}}
if we try to insert that as a custom transformer parameter, it won't resolve. (I've written up an issue on the WireMock github about this.)
Unfortunately, I think this leaves us with having to write a custom extension that will take the request and find the value and then modify the response file. More information on creating a custom transformer extensions can be found here.
At last I created my own transformer:
package com.company.department.app.extensions;
import com.github.tomakehurst.wiremock.common.FileSource;
import com.github.tomakehurst.wiremock.extension.Parameters;
import com.github.tomakehurst.wiremock.extension.ResponseTransformer;
import com.github.tomakehurst.wiremock.http.Request;
import com.github.tomakehurst.wiremock.http.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
public class NLGResponseTransformer extends ResponseTransformer {
private static final Logger LOG = LoggerFactory.getLogger(NLGResponseTransformer.class);
private static final String SLIA_FILE = "/stubs/__files/slia.xml";
private static final String REQ_IMSI_XPATH = "/svc_init/slir/msids/msid";
private static final String[] RES_IMSI_XPATHS = {
"/svc_result/slia/pos/msid",
"/svc_result/slia/company_mlp320_slia/company_netinfo/company_ms_netinfo/msid"
};
private static final String[] RES_TIME_XPATHS = {
// for slia.xml
"/svc_result/slia/company_mlp320_slia/company_netinfo/company_ms_netinfo/time",
// for slia_poserror.xml
"/svc_result/slia/pos/poserror/time"
};
private static final DocumentBuilderFactory DOCUMENT_BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
private static final String UTC_OFF = "utc_off";
private static final String TRANSFORM_FACTORY_ATTRIBUTE_INDENT_NUMBER = "indent-number";
protected static final String COMPANY_MLP_320_SLIA_EXTENSION_DTD = "company_mlp320_slia_extension.dtd";
protected static final String MLP_SVC_RESULT_320_DTD = "MLP_SVC_RESULT_320.DTD";
#Override
public String getName() {
return "inject-request-values";
}
#Override
public Response transform(Request request, Response response, FileSource fileSource, Parameters parameters) {
Document responseDocument = injectValuesFromRequest(request);
String transformedResponse = transformToString(responseDocument);
if (transformedResponse == null) {
return response;
}
return Response.Builder.like(response)
.but()
.body(transformedResponse)
.build();
}
private Document injectValuesFromRequest(Request request) {
// NOTE: according to quickscan:
// "time" element in the MLP is the time MME reports cell_id to GMLC (NLG), NOT the time when MME got the cell_id.
LocalDateTime now = LocalDateTime.now();
Document responseTemplate = readDocument(SLIA_FILE);
Document requestDocument = readDocumentFromBytes(request.getBody());
if (responseTemplate == null || requestDocument == null) {
return null;
}
try {
injectIMSI(responseTemplate, requestDocument);
injectTime(responseTemplate, now);
} catch (XPathExpressionException e) {
LOG.error("Cannot parse XPath expression {}. Cause: ", REQ_IMSI_XPATH, e);
}
return responseTemplate;
}
private Document readDocument(String inputStreamPath) {
try {
DocumentBuilder builder = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();
// ignore missing dtd
builder.setEntityResolver((publicId, systemId) -> {
if (systemId.contains(COMPANY_MLP_320_SLIA_EXTENSION_DTD) ||
systemId.contains(MLP_SVC_RESULT_320_DTD)) {
return new InputSource(new StringReader(""));
} else {
return null;
}
});
return builder.parse(this.getClass().getResourceAsStream(inputStreamPath));
} catch (Exception e) {
LOG.error("Cannot construct document from resource path. ", e);
return null;
}
}
private Document readDocumentFromBytes(byte[] array) {
try {
DocumentBuilder builder = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();
// ignore missing dtd
builder.setEntityResolver((publicId, systemId) -> {
if (systemId.contains(COMPANY_MLP_320_SLIA_EXTENSION_DTD) ||
systemId.contains(MLP_SVC_RESULT_320_DTD)) {
return new InputSource(new StringReader(""));
} else {
return null;
}
});
return builder.parse(new ByteArrayInputStream(array));
} catch (Exception e) {
LOG.error("Cannot construct document from byte array. ", e);
return null;
}
}
private XPath newXPath() {
return XPathFactory.newInstance().newXPath();
}
private void injectTime(Document responseTemplate, LocalDateTime now) throws XPathExpressionException {
for (String timeXPath: RES_TIME_XPATHS) {
Node timeTarget = (Node) (newXPath().evaluate(timeXPath, responseTemplate, XPathConstants.NODE));
if (timeTarget != null) {
// set offset in attribute
Node offset = timeTarget.getAttributes().getNamedItem(UTC_OFF);
offset.setNodeValue(getOffsetString());
// set value
timeTarget.setTextContent(TIME_FORMAT.format(now));
}
}
}
private void injectIMSI(Document responseTemplate, Document requestDocument) throws XPathExpressionException {
Node imsiSource = (Node) (newXPath().evaluate(REQ_IMSI_XPATH, requestDocument, XPathConstants.NODE));
String imsi = imsiSource.getTextContent();
for (String xpath : RES_IMSI_XPATHS) {
Node imsiTarget = (Node) (newXPath().evaluate(xpath, responseTemplate, XPathConstants.NODE));
if (imsiTarget != null) {
imsiTarget.setTextContent(imsi);
}
}
}
private String transformToString(Document document) {
if (document == null) {
return null;
}
document.setXmlStandalone(true); // make document to be standalone, so we can avoid outputing standalone="no" in first line
TransformerFactory tf = TransformerFactory.newInstance();
Transformer trans;
try {
trans = tf.newTransformer();
trans.setOutputProperty(OutputKeys.INDENT, "no"); // no extra indent; file already has intent of 4
// cannot find a workaround to inject dtd in doctype line. TODO
//trans.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "MLP_SVC_RESULT_320.DTD [<!ENTITY % extension SYSTEM \"company_mlp320_slia_extension.dtd\"> %extension;]");
StringWriter sw = new StringWriter();
trans.transform(new DOMSource(document), new StreamResult(sw));
// Spaces between tags are considered as text node, so when outputing we need to remove the extra empty lines
return sw.toString().replaceAll("\\n\\s*\\n", "\n");
} catch (TransformerException e) {
LOG.error("Cannot transform response document to String. ", e);
return null;
}
}
/**
* Compare system default timezone with UTC and get zone offset in form of (+/-)XXXX.
* Dependent on the machine default timezone/locale.
* #return
*/
private String getOffsetString() {
// getting offset in (+/-)XX:XX format, or "Z" if is UTC
String offset = ZonedDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()).getOffset().toString();
if (offset.equals("Z")) {
return "+0000";
}
return offset.replace(":", "");
}
}
And use it like this:
mvn package it as a JAR(non-runnable), put it aside wiremock standalone jar, for example libs
Run this:
java -cp libs/* com.github.tomakehurst.wiremock.standalone.WireMockServerRunner --extensions com.company.department.app.extensions NLGResponseTransformer --https-port 8443 --verbose
Put the whole command on the same line.
Notice the app jar which contains this transformer and wiremock standalone jar should be among classpath. Also, other dependencies under libs are needed. (I use jib maven plugin which copies all dependencies under libs/; I also move app and wiremock jars to libs/, so I can put "-cp libs/*"). If that does not work, try to specify the location of these two jars in -cp. Be ware that Wiremock will runs OK even when the extension class is not found. So maybe add some loggings.
You can use --root-dir to point to stubs files root, for example --root-dir resources/stubs in my case. By default it points to .(where java runs).

getting cause:null in property dlqDeliveryFailureCause

I am trying to set up Dead Letter Queue monitoring for a system. So far, I can get it to be thrown in the DLQ queue without problems when the message consumption fails on the consumer. Now I'm having some trouble with getting the reason why it failed;
currently I get the following
java.lang.Throwable: Delivery[2] exceeds redelivery policy imit:RedeliveryPolicy
{destination = queue://*,
collisionAvoidanceFactor = 0.15,
maximumRedeliveries = 1,
maximumRedeliveryDelay = -1,
initialRedeliveryDelay = 10000,
useCollisionAvoidance = false,
useExponentialBackOff = true,
backOffMultiplier = 5.0,
redeliveryDelay = 10000,
preDispatchCheck = true},
cause:null
I do not know why cause is coming back as null. I'm using Spring with ActiveMQ. I'm using the DefaultJmsListenerContainerFactory, which creates a DefaultMessageListenerContainer. I would like cause to be filled with the exception that happened on my consumer but I can't get it to work. Apparently there's something on Spring that's not bubbling up the exception correctly, but I'm not sure what it is. I'm using spring-jms:4.3.10. I would really appreciate the help.
I am using spring-boot-starter-activemq:2.2.2.RELEASE (spring-jms:5.2.2, activemq-client-5.15.11) and I have the same behavior.
(links point to the versions I use)
The rollback cause is added here for the POSION_ACK_TYPE (sic!).
Its assignment to the MessageDispatch is only happening in one place: when dealing with a RuntimeException in the case there is a javax.jms.MessageListener registered.
Unfortunately (for this particular case), Spring doesn't register one, because it prefers to deal with its own hierarchy. So, long story short, there is no chance to make it happen with Spring out-of-the-box.
However, I managed to write an hack-ish way of getting an access to the MessageDispatch instance dealt with, inject the exception as the rollback cause, and it works!
package com.example;
import org.springframework.jms.listener.DefaultMessageListenerContainer;
import javax.jms.*;
public class MyJmsMessageListenerContainer extends DefaultMessageListenerContainer {
private final MessageDeliveryFailureCauseEnricher messageDeliveryFailureCauseEnricher = new MessageDeliveryFailureCauseEnricher();
private MessageConsumer messageConsumer; // Keep for later introspection
#Override
protected MessageConsumer createConsumer(Session session, Destination destination) throws JMSException {
this.messageConsumer = super.createConsumer(session, destination);
return this.messageConsumer;
}
#Override
protected void invokeListener(Session session, Message message) throws JMSException {
try {
super.invokeListener(session, message);
} catch (Throwable throwable) {
messageDeliveryFailureCauseEnricher.enrich(throwable, this.messageConsumer);
throw throwable;
}
}
}
Note: don't deal with the Throwable by overriding the protected void handleListenerException(Throwable ex) method, because at that moment some cleanup already happened in the ActiveMQMessageConsumer instance.
package com.example;
import org.apache.activemq.ActiveMQMessageConsumer;
import org.apache.activemq.command.MessageDispatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;
import javax.jms.MessageConsumer;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
class MessageDeliveryFailureCauseEnricher {
private static final Logger logger = LoggerFactory.getLogger(MessageDeliveryFailureCauseEnricher.class);
private final Map<Class<?>, Field> accessorFields = new HashMap<>();
private final Field targetField;
public MessageDeliveryFailureCauseEnricher() {
this.targetField = register(ActiveMQMessageConsumer.class, "deliveredMessages");
// Your mileage may vary; here is mine:
register("brave.jms.TracingMessageConsumer", "delegate");
register("org.springframework.jms.connection.CachedMessageConsumer", "target");
}
private Field register(String className, String fieldName) {
Field result = null;
if (className == null) {
logger.warn("Can't register a field from a missing class name");
} else {
try {
Class<?> clazz = Class.forName(className);
result = register(clazz, fieldName);
} catch (ClassNotFoundException e) {
logger.warn("Class not found on classpath: {}", className);
}
}
return result;
}
private Field register(Class<?> clazz, String fieldName) {
Field result = null;
if (fieldName == null) {
logger.warn("Can't register a missing class field name");
} else {
Field field = ReflectionUtils.findField(clazz, fieldName);
if (field != null) {
ReflectionUtils.makeAccessible(field);
accessorFields.put(clazz, field);
}
result = field;
}
return result;
}
void enrich(Throwable throwable, MessageConsumer messageConsumer) {
if (throwable != null) {
if (messageConsumer == null) {
logger.error("Can't enrich the MessageDispatch with rollback cause '{}' if no MessageConsumer is provided", throwable.getMessage());
} else {
LinkedList<MessageDispatch> deliveredMessages = lookupFrom(messageConsumer);
if (deliveredMessages != null && !deliveredMessages.isEmpty()) {
deliveredMessages.getLast().setRollbackCause(throwable); // Might cause problems if we prefetch more than 1 message
}
}
}
}
private LinkedList<MessageDispatch> lookupFrom(Object object) {
LinkedList<MessageDispatch> result = null;
if (object != null) {
Field field = accessorFields.get(object.getClass());
if (field != null) {
Object fieldValue = ReflectionUtils.getField(field, object);
if (fieldValue != null) {
if (targetField == field) {
result = (LinkedList<MessageDispatch>) fieldValue;
} else {
result = lookupFrom(fieldValue);
}
}
}
}
return result;
}
}
The magic happen in the second class:
At construction time we make some private fields accessible.
When a Throwable is caught, we traverse these fields to end up with the appropriate MessageDispatch instance (beware if you prefetch more than 1 message), and inject it the throwable we want to be part of the dlqDeliveryFailureCause JMS property.
I crafted this solution this afternoon, after hours of debugging (thanks OSS!) and many trials and errors. It works, but I have the feeling it's more of an hack than a real, solid solution.
With that in mind, I made my best to avoid side effects, so the worst that can happen is no trace of the original Throwable in the message ending in the Dead Letter Queue.
If I missed the point somewhere, I'b be glad to learn more about this.

How to get a session by session id in JBoss 5

I want to verify that session in JBoss 5 is still active and in logged in state. to implement a JWT (json web token).
for this, I need to get session by id.
to debug it: JBoss uses a special version of tomcat called JBoss web.
then I searched "jboss web 2* jar" and added it as source for sources to eclipse then I could debug it. also in eclipse, I have installed from eclipse marketplace FernFlower decompiler (* I took the actual version from https://developer.jboss.org/wiki/VersionOfTomcatInJBossAS)
I referenced those sources
how to refresh JSESSIONID cookie after login
https://github.com/auth0/java-jwt
my solution may help other pseudo tomcat serverlet servers
package com.mysoftware.controller.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.mysoftware.util.SqlInjectionAndXSSRequestWrapper;
import com.mysoftware.model.User;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Arrays;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestWrapper;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.catalina.Manager;
import org.apache.catalina.Session;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.RequestFacade;
import org.jboss.seam.security.Identity;
import org.jboss.seam.web.ServletContexts;
import org.jboss.util.Base64;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Cipher;
public class JWTAuthorization {
public static String isSessionIdLoggedIn(String requestedSessionId) {
try {
// get the request
HttpServletRequest request =ServletContexts.instance().getRequest();
ServletRequest serverletRequest = ((ServletRequestWrapper)request).getRequest();
// first need to unwrap the request until the core - org.apache.catalina.connector.Request.
// i had additional wrapper around the request SqlInjectionAndXSSRequestWrapper. (you probably wont have it)
// for simplicity i added SqlInjectionAndXSSRequestWrapper.request to my class, just saved the constructor argument.
SqlInjectionAndXSSRequestWrapper injectionRequest = (SqlInjectionAndXSSRequestWrapper) serverletRequest;
// code may start here, I run it and cast it and debug it and when I see it crash: "can't convert class x to y'. I understand which class it is and unwrap it accordingly.
RequestFacade requestFacade = (RequestFacade) injectionRequest.request;
Field catalinaRequestField;
//Getting actual catalina request using reflection
catalinaRequestField = requestFacade.getClass().getDeclaredField( "request" );
catalinaRequestField.setAccessible( true ); // grant access to (protected) field
Request realRequest = (Request)catalinaRequestField.get( requestFacade );
Manager manager = realRequest.getContext().getManager();
HttpSession session = null;
try {
session=(HttpSession) manager.findSession(requestedSessionId);
} catch (IOException var7) {}
if (session != null && !((Session) session).isValid()) {session = null;}
if (session != null) {((Session) session).access();} // mark usage
if (session != null && session.isNew()) return "new";
if (session != null )
{
Identity identity = (Identity)session.getAttribute("org.jboss.seam.security.identity");
if (identity != null && identity.isLoggedIn()) return "login";
}
return "not login";
} catch (Exception e1) {
e1.printStackTrace();
return "exception";
}
}
protected final static String sessionidencryptionkey="1234567890ghdg";
protected final static String jwtsecret="1234567890sdghsg";
public static String getTokenForCRM(User user)
{
try {
Algorithm algorithm = Algorithm.HMAC256(jwtsecret);
String token = JWT.create()
.withSubject(user.getId().toString())
.withArrayClaim("CRM", new String[]{ user.getAccount().getCrm() } )
.withClaim("SessionID", encrypt( ServletContexts.instance().getRequest().getSession().getId() , sessionidencryptionkey) )
.sign(algorithm);
return token;
} catch (Exception exception){
//Invalid Signing configuration / Couldn't convert Claims.
}
return "ERROR_CREATEING_TOKEN";
}
public static String getSessionId(DecodedJWT token)
{
try {
return decrypt( token.getClaim("SessionID").asString() , sessionidencryptionkey) ;
} catch (Exception e) {
//e.printStackTrace();
return null;
}
}
public static DecodedJWT verifyToken(String token)
{
try {
Algorithm algorithm = Algorithm.HMAC256(jwtsecret);
JWTVerifier verifier = JWT.require(algorithm)
//.withIssuer("auth0")
.build(); //Reusable verifier instance
DecodedJWT jwt = verifier.verify(token);
return jwt;
} catch (JWTVerificationException exception){
//Invalid signature/claims
}
return null;
}
public static String encrypt(String strClearText,String strKey) throws Exception{
String strData="";
try {
SecretKeySpec skeyspec=new SecretKeySpec(strKey.getBytes(),"Blowfish");
Cipher cipher=Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, skeyspec);
byte[] encrypted=cipher.doFinal(strClearText.getBytes());
strData=new String(encrypted);
//strData=Base64.encodeBytes(encrypted);
} catch (Exception e) {
e.printStackTrace();
throw new Exception(e);
}
return strData;
}
public static String decrypt(String strEncrypted,String strKey) throws Exception{
String strData="";
try {
SecretKeySpec skeyspec=new SecretKeySpec(strKey.getBytes(),"Blowfish");
Cipher cipher=Cipher.getInstance("Blowfish");
cipher.init(Cipher.DECRYPT_MODE, skeyspec);
final byte[] strEncryptedBytes=strEncrypted.getBytes();
// final byte[] strEncryptedBytes==Base64.encode(strEncrypted)
byte[] decrypted=cipher.doFinal(strEncryptedBytes);
strData=new String(decrypted);
} catch (Exception e) {
e.printStackTrace();
throw new Exception(e);
}
return strData;
}
}
my testing code was.
inside a controller i had:
call it without wuery on different browsers,
then add parameter of the other session id in one of the browsers
#GET
#Path("testJWTSessionCheck")
#Produces("application/json")
public String testJWTSessionCheck( #QueryParam("s") String requestedSessionId) {
if(requestedSessionId!=null && requestedSessionId.length()>5) {
JWTAuthorization.isSessionIdLoggedIn(requestedSessionId);
}
HttpSession session1 = ServletContexts.instance().getRequest().getSession(false);
return session1.getId();
}

Mockito Unit Test with HTTPUrlConnection

I wrote code in our Spring Boot 2 application to make a third-party API call with HTTPUrlConnection.
public String loginApi(LoginDTO loginDto)
{
String responseData = null;
HttpURLConnection conn = null;
try {
link = authBaseUrl + loginUrl;
url = new URL(link);
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty(CONTENT_TYPE, MEDIA_TYPE);
String body = getAuth0LoginDto(loginDto);
// =====================
// For POST only - START
conn.setDoOutput(true);
OutputStream os = conn.getOutputStream();
os.write(body.getBytes(StandardCharsets.UTF_8));
os.flush();
os.close();
// For POST only - END
// ====================
try (BufferedReader br = (conn.getResponseCode() >= 400
? new BufferedReader(new InputStreamReader(conn.getErrorStream()))
: new BufferedReader(new InputStreamReader(conn.getInputStream())))) {
StringBuilder everything = new StringBuilder();
String output = null;
while ((output = br.readLine()) != null) {
everything.append(output);
}
responseData = everything.toString();
}
} catch (JsonProcessingException e) {
throw new Auth0Exception("Could not create Auth0 Login Body", e);
} catch (IOException e) {
throw new Auth0Exception("Error with Login API", e);
} finally {
if (conn != null) {
conn.disconnect();
}
}
return responseData;
}
Now, I am very much used to doing real integration testing, where I make a real call to the web-service and check the results.
I am now being asked to use strictly Mockito, not PowerMockito, not EasyMock, to create mocking tests, and I have never done that before. My knowledge of Mockito is weak also since I haven't used it in a very long time.
So, I know it has been asked before, and I have really searched on the internet, and I really haven't found a full piece of code as an example. I see code snippets which leaves me with pieces missing, and I am not knowledgeable enough to add those parts myself.
I know this code actual implementation works fine, and the integration test works fine also. But, what I have seen before is that some users are being told they need to change their client code in order to make the mockito tests work.
If I don't get the mocking tests working for HTTPUrlConnection, then I'll be forced to switch over to RestTemplate and Mocking since my co-worker insists we use RestTemplate anyway.
Thanks!
Since you have asked for a small example which does not make sense but should show the idea:
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.HttpURLConnection;
import java.net.URL;
public class App {
public int status(URL url) {
HttpURLConnection urlConnection = null;
try {
urlConnection = create(url);
return urlConnection.getResponseCode();
} catch (IOException e) {
throw new UncheckedIOException(e);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
HttpURLConnection create(URL url) throws IOException {
return (HttpURLConnection) url.openConnection();
}
}
I would implement this with a spy and as I recommended a mocked HttpURLConnection:
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
#ExtendWith(MockitoExtension.class)
class AppTest {
#Spy
App app;
#Mock
HttpURLConnection connection;
#Test
void status() throws IOException {
int expected = 200;
doReturn(connection).when(app).create(any());
doReturn(expected).when(connection).getResponseCode();
URL url = new URL("http://www.google.ats");
int status = app.status(url);
Assertions.assertEquals(expected, status);
}
}

Netty ProxyHandler writeAndFlush is not writing response to server

Am trying to implement an NTLMProxyHandler in Netty that can perform the NTLM message exchanges and authenticate the client with a web proxy.
The NTLMProxyHandler extends Netty's ProxyHandler class. Due to this an initial HTTP request is triggered by the proxy handler and this reaches the mock proxy server that I have created. The proxy server reads this request and responds with a 407 proxy authentication required response.
The NTLMProxyHandler reads this response on the client side and prepares a new NTLM Type1Message and writes the response to server back again. The problem I am facing is that this request is never sent to my proxy server though the channel future's success handler is called.
I have enabled Netty packages in the logging but unable to figure out why only the response written second time from the ntlm proxy handler is lost.
I have tried using the Netty ProxyHandler's sendToProxyServer(msg) as well as using the channelHandlerCtx passed from channelRead(). In both the cases writeAndFlush is done but the response never reaches the server and the server times out.
Has anyone used the channelHandlerCtx to write back a response to the server and perform a message exchange similar to this ?
Why is the initial request from ntlm proxy handler -> server successful but not successive reponses written from this ntlm proxy handler.
I also see while debugging that even if I shutdown the proxy server while writing the NTLMMessage1, the writeAndFlush future is still successful. Why would the writeAndFlush succeed in this case ?
Any pointers will be really helpful. Thanks !
NTLMProxyHandler.java
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.proxy.ProxyConnectException;
import jcifs.ntlmssp.Type1Message;
import jcifs.ntlmssp.Type2Message;
import jcifs.ntlmssp.Type3Message;
import jcifs.smb.NtlmContext;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.util.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
public class NTLMProxyHandler extends AbstractProxyHandler {
private String userName;
private String password;
private final static String DOMAIN = "CORP";
public static final String NTLM_Prefix = "NTLM";
private static final Logger logger = LoggerFactory.getLogger(NTLMProxyHandler.class);
private static int NTLMV2_FLAGS_TYPE3 = 0xa2888205;
private HttpResponseStatus status;
private HttpResponse response;
private NtlmPasswordAuthentication ntlmPasswordAuthentication;
private NtlmContext ntlmContext;
private final HttpClientCodec codec = new HttpClientCodec();
public NTLMProxyHandler(SocketAddress proxyAddress) {
super(proxyAddress);
}
public NTLMProxyHandler(SocketAddress proxyAddress, String domain, String username, String password) {
super(proxyAddress);
setConnectTimeoutMillis(50000);
this.userName = username;
this.password = password;
ntlmPasswordAuthentication = new NtlmPasswordAuthentication(DOMAIN, username, password);
ntlmContext = new NtlmContext(ntlmPasswordAuthentication, true);
}
#Override
public String protocol() {
return "http";
}
#Override
public String authScheme() {
return "ntlm";
}
protected void addCodec(ChannelHandlerContext ctx) throws Exception {
ChannelPipeline p = ctx.pipeline();
String name = ctx.name();
p.addBefore(name, (String)null, this.codec);
}
protected void removeEncoder(ChannelHandlerContext ctx) throws Exception {
this.codec.removeOutboundHandler();
}
protected void removeDecoder(ChannelHandlerContext ctx) throws Exception {
this.codec.removeInboundHandler();
}
#Override
protected Object newInitialMessage(ChannelHandlerContext channelHandlerContext) throws Exception {
InetSocketAddress raddr = this.destinationAddress();
String rhost;
if(raddr.isUnresolved()) {
rhost = raddr.getHostString();
} else {
rhost = raddr.getAddress().getHostAddress();
}
String host = rhost + ':' + raddr.getPort();
DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.CONNECT, host, Unpooled.EMPTY_BUFFER, false);
req.headers().set(HttpHeaderNames.HOST, host);
req.headers().set("connection", "keep-alive");
// This initial request successfully reaches the server !
return req;
}
#Override
protected boolean handleResponse(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
if (o instanceof HttpResponse) {
response = (HttpResponse) o;
}
boolean finished = o instanceof LastHttpContent;
if(finished) {
status = response.status();
logger.info("Status: " + status);
if (!response.headers().isEmpty()) {
for (String name: response.headers().names()) {
for (String value: response.headers().getAll(name)) {
logger.debug("Header: " + name + " = " + value);
}
}
}
if(status.code() == 407) {
negotiate(channelHandlerContext, response);
}
else if(status.code() == 200){
logger.info("Client: NTLM exchange complete. Authenticated !");
}
else {
throw new ProxyConnectException(this.exceptionMessage("status: " + this.status));
}
}
return finished;
}
private void negotiate(ChannelHandlerContext channelHandlerContext, HttpResponse msg) throws Exception{
String ntlmHeader = msg.headers().get(HttpHeaderNames.PROXY_AUTHENTICATE);
if(ntlmHeader.equalsIgnoreCase("NTLM")){
logger.info("Client: Creating NTLM Type1Message");
//Send Type1Message
byte[] rawType1Message = ntlmContext.initSecContext(new byte[]{}, 0, 0);
Type1Message type1Message = new Type1Message(rawType1Message);
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
String proxyAuthHeader = Base64.encode(type1Message.toByteArray());
logger.info("Setting proxyAuthHeader = " + proxyAuthHeader);
response.headers().set(HttpHeaders.Names.PROXY_AUTHORIZATION, proxyAuthHeader);
ByteBuf byteBuf = Unpooled.buffer(rawType1Message.length);
byteBuf.writeBytes(response.content());
//This is where the response is lost and never reaches the proxy server
sendToProxyServer(byteBuf);
// channelHandlerContext.writeAndFlush(response.content));
} else if (ntlmHeader.contains(NTLM_Prefix)) {
logger.info("Client: Creating NTLM Type3Message");
//Send Type3 Message
}
}
}
I finally figured out the problem. The NTLM proxy handler when it responds to the proxy's message was sending a FullHTTPResponse instead of FullHTTPRequest. Looks like Netty's pipeline was discarding the data written as response and this was not indicated in the logs.
DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.CONNECT, host, Unpooled.EMPTY_BUFFER, false);
req.headers().set(HttpHeaderNames.HOST, host);
req.headers().set(HttpHeaders.Names.PROXY_AUTHORIZATION, "type3message");
sendToProxyServer(req);

Resources