Netty client server login, how to have channelRead return a boolean - authentication

I'm writing client server applications on top of netty.
I'm starting with a simple client login server that validates info sent from the client with the database. This all works fine.
On the client-side, I want to use If statements once the response is received from the server if the login credentials validate or not. which also works fine. My problem is the ChannelRead method does not return anything. I can not change this. I need it to return a boolean which allows login attempt to succeed or fail.
Once the channelRead() returns, I lose the content of the data.
I tried adding the msg to a List but, for some reason, the message data is not stored in the List.
Any suggestions are welcome. I'm new... This is the only way I've figured out to do this. I have also tried using boolean statements inside channelRead() but these methods are void so once it closes the boolean variables are cleared.
Following is the last attempt I tried to insert the message data into the list I created...
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class LoginClientHandler extends ChannelInboundHandlerAdapter {
Player player = new Player();
String response;
public volatile boolean loginSuccess;
// Object message = new Object();
private Object msg;
public static final List<Object> incomingMessage = new List<Object>() {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// incomingMessage.clear();
response = (String) msg;
System.out.println("channel read response = " + response);
incomingMessage.add(0, msg);
System.out.println("incoming message = " + incomingMessage.get(0));
}
How can I get the message data "out" of the channelRead() method or use this method to create a change in my business logic? I want it to either display a message to tell the client login failed and try again or to succeed and load the next scene. I have the business logic working fine but I can't get it to work with netty because none of the methods return anything I can use to affect my business logic.
ChannelInitializer
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
public class LoginClientInitializer extends ChannelInitializer <SocketChannel> {
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handler", new LoginClientHandler());
}
}

To get the server to write data to the client, call ctx.write here is a basic echo server and client example from the Netty in Action book. https://github.com/normanmaurer/netty-in-action/blob/2.0-SNAPSHOT/chapter2/Server/src/main/java/nia/chapter2/echoserver/EchoServerHandler.java
There are several other good examples in that repo.
I highly recommend reading the "netty in action" book if you're starting out with netty. It will give you a solid foundational understanding of the framework and how it's intended to be used.

Related

Use Java8 Stream on JDBCTemplate Results from HIVE

I am using jdbcTemplate to query hive then writing the results to a .csv file. I basically just generate a list of objects then steam the list to write each record to the file.
I will like to stream the results as they coming back from hive and write it to the file instead of wait to get the whole thing then processing it. Can anyone pointing me to the right direction? Thanks!
private List<Avs> queryAvsData(String asSql) {
List<Avs> llistAvs = new ArrayList<Avs>();
List<Map<String, Object>> rows = hiveJdbcTemplate.queryForList(asSql);
Iterator<Map<String, Object>> it = rows.iterator();
while (it.hasNext()) {
Map<String, Object> row = it.next();
Avs laAvs = Avs.builder()
.make((String) row.get("make"))
.model((String) row.get("model"))
.build();
llistAvs.add(laAvs);
}
return llistAvs;
}
It doesn't look like there's a built-in solution, but you can do it. Basically, you wrap the existing functionality in an iterator, and use a spliterator to turn it into a stream. Here's a blog post on the subject:
The code implements Spring’s ResultSetExtractor interface, which is a Single Abstract Method (SAM) interface, allowing the use of a lambda expression to implement it.
The implementation wraps the SQL ResultSet in an iterator, constructs a stream using the Spliterators and StreamSupport utility classes, and applies that to a Function taking a stream of row sets and returning a generic result.
It's possible to stream values from JdbcTemplate. The following example is a service based on Spring Boot 2.4.8.
As, I run into problems (connection leak) using queryForStream then I will put a demo code here just to know that stream must be closed after usage.
import lombok.RequiredArgsConstructor;
import org.springframework.jdbc.core.SingleColumnRowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.stream.Stream;
#Service
#RequiredArgsConstructor
public class DataCleaningService {
private final NamedParameterJdbcTemplate jdbcTemplate;
public void doSomeStreaming() {
String nativeQuery = "SELECT string_value FROM my_table WHERE column = :valueToFiler";
Map<String, Object> queryParameters = Map.of("valueToFiler", "my value");
SingleColumnRowMapper<String> stringRowMapper = SingleColumnRowMapper.newInstance(String.class);
try (Stream<String> stringValueStream = jdbcTemplate.queryForStream(nativeQuery, queryParameters, stringRowMapper)) {
stringValueStream.forEach(stringValue -> {
// do the needed action with the value
//..
System.out.printf("My cool value: %s", stringValue);
});
}
}
}

Modify remote driver URL at runtime

I have a project which is based on the serenity-bdd/serenity-cucumber-starter project. I'm using test-containers to start a couple of Docker containers as well as a Selenium Grid container to run the test against.
new GenericContainer<>(SELENIUM_IMAGE)
...
.withExposedPorts(SELENIUM_CONTAINER_PORT, SELENIUM_CONTAINER_NOVNC_PORT)
...
);
When the tests start, test-containers will ramp up the containers and bind random host ports to all exposed ports of the containers.
Because of that, I cannot define a fixed value in serenity.conf for the url of the remote driver
webdriver.remote.url = "http://localhost:????/wd/hub"
Thus I need a way to set webdriver.remote.url programmatically.
One option would be to use the FixedHostPortGenericContainer, which allows you define the host port on which the container exposed port will be bound to.
I'd rather would like to use a different approach though, as the developers state that
While this works, we strongly advise against using fixed ports, since this will automatically lead to integrated tests (which are an anti pattern).
So the question is: How can I modify the value of webdriver.remote.url at runtime? Is there any option provided by serenity-bdd to reload the net.thucydides.core.util.SystemEnvironmentVariables at runtime?
Faced recently the same issue, but was lucky enough to find a solution:
import java.net.MalformedURLException;
import java.net.URL;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.RemoteWebDriver;
import net.serenitybdd.core.webdriver.driverproviders.FirefoxDriverCapabilities;
import net.thucydides.core.guice.Injectors;
import net.thucydides.core.util.EnvironmentVariables;
import net.thucydides.core.webdriver.DriverSource;
public class CustomWebDriverFactory implements DriverSource {
#Override
public WebDriver newDriver() {
try {
String ip = "your_dynamic_ip";
return new RemoteWebDriver(
new URL("http://" + ip + ":4444/wd/hub"),
new FirefoxDriverCapabilities(Injectors.getInjector().getProvider(EnvironmentVariables.class).get()).getCapabilities());
}
catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
#Override
public boolean takesScreenshots() {
return true;
}
}
So you have to add such factory implementation and define in serenity.properties:
webdriver.driver = provided
webdriver.provided.type = mydriver
webdriver.provided.mydriver = <your_factory_package>.CustomWebDriverFactory
thucydides.driver.capabilities = mydriver

Use of RabbitTemplate.convertSendAndReceive with org.springframework.messaging.Message

I have successfully used the following to send an org.springframework.amqp.core.Message and receive a byte []
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
Message message =
MessageBuilder.withBody(payload)..setCorrelationIdString(id).build();
byte [] response = (byte[]) rabbitTemplate.convertSendAndReceive(message,m -> {
m.getMessageProperties().setCorrelationIdString(id);
This works fine if the queues are set up to handle the message correctly for Message<?>. But I have a series of queues that use the message type org.springframework.messaging.Message specifically Message<String>.
Is there a way I can use rabbitTemplate.convertSendAndReceive to send the org.springframework.messaging.Message Message< String>. Such that the following would work.
import org.springframework.messaging.Message;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
Message<String> message =
MessageBuilder.withPayload(payload).setCorrelationId(id).build();
Object returnObject = rabbitTemplate.convertSendAndReceive(message);
I have looked at the MessageConverter but I am unsure if I can use that.
Alternatively, should I use org.springframework.messaging.core.GenericMessagingTemplate.convertSendAndReceive
UPDATE.
I can make it work if I change what I have on the queues from
#Transformer(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT)
public Message<String> transform(Message<String> inMessage) {
to
#Transformer(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT)
public Message<String> transform(Message<?> inMessage) { GenericMessage<?>
genericMessage = (GenericMessage<?>)inMessage.getPayload();
String payload = (String)genericMessage.getPayload();
but I would rather not have to change the transformers to make this work as the code in question is for integration tests and existing code already works with what I already have.
END UPDATE
I think I have given enough information but please let me know if more details are required. Ideally, I am looking for a code example or to point me to the documentation that answers my question.
Use the RabbitMessagingTemplate documentation here.
public Message<?> sendAndReceive(String exchange, String routingKey, Message<?> requestMessage)

Java XmlRpc Client POST issue with TCL XMLRPC Server

I am having a simple problem, my Java XMLRPC Client cant seem to properly speak to the XMLRPC Server that is written in TCL
(Using TCL XMLRPC SERVER OPEN SOURCE implementation)
Summary: XMLRPC Clients in TCL/Python etc, can/do send/receive messages to the TCL XMLRPC Server, but my Java XMLRPC client doenst seem to work.
Java Client Side Code:
/*
* try's, catches, comments removed to show code-flow w/ out mess.
* host/port/target all same as whats set in Python
*/
//show imports / package used, this is using apache's xmlrpc v3.1.3
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
//... host and port are the same as whats used in working tcl/python clients. (remoteHostName / 5555)
//... method is the same as well, 'fooBar123', and args is just 1 string passed to it.
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
target = "RPC2";
String targetUrl = "http://"+host+":"+port+"/" + target;
TestNgUtil.ReportInfo("config.SetServerUrl("+targetUrl+")");
config.setServerURL(new URL(targetUrl));
XmlRpcClient client = new XmlRpcClient();
client.setConfig(config);
String result = null;
/*
* This Result Never Returns from TCL XMLRPC Server
*/
result = (String) client.execute(command, params);
TCL Server's Debug ERROR Response to Java:
//(notice unlike Python example below, no proper Header, Content-Type, etc)
TCL Server Side of the Java Error
in serveOnce: addr: 10.21.69.13
in serveOnce: port: 64522
Unknown type: fooBar123</value></param></params>
bgerror failed to handle background error.
Original error:
Error in bgerror: can't read "xmlcall": no such variable**
Python example works, however, also note I print out the XML-debug to look at successful requests:
However, If I attempt to use the TCL client, or even a simple Python XMLRPC client, it works.
I even use Python to print out the XMLRPC request:
(from Python client, nothing fancy, )
import xmlrpclib
server_url = "http://remoteHostName:5555";
server = xmlrpclib.Server(server_url, verbose=True);
result = server.hello('hello world')
## DEBUG INFO PRINTED FROM REQUEST POST ##
send: "POST /RPC2 HTTP/1.1\r\nHost: remoteHostName:5555\r\nAccept-Encoding: gzip\r\nUser-Agent: xmlrpclib.py/1.0.1 (by www.pythonware.com)\r\nContent-Type: text/xml\r\nContent-Length: 160\r\n\r\n<?xml version='1.0'?>\n<methodCall>\n<methodName>hello</methodName>\n<params>\n<param>\n<value><string>hello world</string></value>\n</param>\n</params>\n</methodCall>\n"
reply: 'HTTP/1.1 200 OK\n'
header: Content-Type: text/xml
header: Content-length: 162
body: '<?xml version="1.0"?>\n<methodResponse>\n\t<params>\n\t\t<param>\n\t\t\t<value> <string>hello(hello world) yaaaah?!</string></value>\n\t\t</param>\n\t</params>\n</methodResponse>\n'
TCL Server's Debug/Response to Python, before pushing back proper response:
send: "POST /RPC2 HTTP/1.1
Host: remoteHostName:5555
Accept-Encoding: gzip
User-Agent: xmlrpclib.py/1.0.1 (by www.pythonware.com)
Content-Type: text/xml
Content-Length: 156
here's the TCL XMLRPC Server code for hello( arg ), works for tcl, python, not java. (java client configuration issue probably)
#using the TCL XMLRPC Server ( http://sourceforge.net/projects/xmlrpctcl/ )
package require xmlrpc
xmlrpc::serv 5555
proc hello { world } {
puts "IN HELLO WORLD!"
set res "hello(${world}) yaaaah?!"
return [list string $res]
}
vwait forever
I appreciate any pointers, I've gone so far to attempt to use Java or Python with embedded TCL interpretters to avoid this, but because of the large amount of TCL that this application has to use, source, and share, I have to get a TCL XMLRPC server up and working.
I've also attempted to use the webservices httpd with XMLRPC, but did not have much sucess even with getting it to work with tcl/python clients.
Killed a whole weekend on this already.
Thanks for reading, and any pointers/help.
SOLUTION FOUND (POSTED HERE)
the problem boils down to the XML of the old tcl xmlrpc server not including on the data types. They're implied, so in order to get apache's XMLRPC client to send the implied data types around strings, just implement the 'custom data type' to put the tag back.
CODE IS HERE:
import java.net.URL;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
import org.apache.xmlrpc.common.TypeFactoryImpl;
import org.apache.xmlrpc.common.XmlRpcController;
import org.apache.xmlrpc.common.XmlRpcStreamConfig;
import org.apache.xmlrpc.serializer.StringSerializer;
import org.apache.xmlrpc.serializer.TypeSerializer;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
public class XMLRPCClient {
public static void main(String[] argv) throws Exception {
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
config.setServerURL(new URL("http://127.0.0.1:6800/rpc"));
XmlRpcClient client = new XmlRpcClient();
client.setConfig(config);
client.setTypeFactory(new MyTypeFactoryImpl(client));
Object[] params = new Object[] {
new String[] { "http://www.google.com" }
};
String result = (String)client.execute("aria2.addUri", params);
}
static private class MyStringSerializer extends StringSerializer {
public void write(ContentHandler pHandler, Object pObject)
throws SAXException {
// Write <string> tag explicitly
write(pHandler, STRING_TAG, pObject.toString());
}
}
static private class MyTypeFactoryImpl extends TypeFactoryImpl {
public MyTypeFactoryImpl(XmlRpcController pController) {
super(pController);
}
public TypeSerializer getSerializer(XmlRpcStreamConfig pConfig, Object pObject) throws SAXException {
if(pObject instanceof String) {
return new MyStringSerializer();
} else {
return super.getSerializer(pConfig, pObject);
}
}
}
}
The problem boils down to the XML of the old tcl XMLRPC server not including on the data types. They're implied, so in order to get Apache's XMLRPC client to send the implied data types around strings just implement the 'custom data type' to put the tag back.
import java.net.URL;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
import org.apache.xmlrpc.common.TypeFactoryImpl;
import org.apache.xmlrpc.common.XmlRpcController;
import org.apache.xmlrpc.common.XmlRpcStreamConfig;
import org.apache.xmlrpc.serializer.StringSerializer;
import org.apache.xmlrpc.serializer.TypeSerializer;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
public class XMLRPCClient {
public static void main(String[] argv) throws Exception {
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
config.setServerURL(new URL("http://127.0.0.1:6800/rpc"));
XmlRpcClient client = new XmlRpcClient();
client.setConfig(config);
client.setTypeFactory(new MyTypeFactoryImpl(client));
Object[] params = new Object[] {
new String[] { "http://www.google.com" }
};
String result = (String)client.execute("aria2.addUri", params);
}
static private class MyStringSerializer extends StringSerializer {
public void write(ContentHandler pHandler, Object pObject)
throws SAXException {
// Write <string> tag explicitly
write(pHandler, STRING_TAG, pObject.toString());
}
}
static private class MyTypeFactoryImpl extends TypeFactoryImpl {
public MyTypeFactoryImpl(XmlRpcController pController) {
super(pController);
}
public TypeSerializer getSerializer(XmlRpcStreamConfig pConfig, Object pObject) throws SAXException {
if(pObject instanceof String) {
return new MyStringSerializer();
} else {
return super.getSerializer(pConfig, pObject);
}
}
}
}

Using JNDI to access a DataSource (Tomcat 6)

I have been trying to set up a Database Connection Pool for my test webapp just to learn how it's done really. I have managed to get a DataSource object connected to my database which supplies me with Connection objects now, so that's good.
I must admit I don't really know exactly how it's working. I wrote some test code to see if I could figure out how the InitialContext object is working:
package twittersearch.web;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import java.sql.*;
import javax.sql.*;
import javax.naming.*;
import twittersearch.model.*;
public class ContextTest extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
Context ctx = null;
Context env = null;
try {
ctx = new InitialContext();
Hashtable<?, ?> h = ctx.getEnvironment();
Enumeration<?> keyEn = h.keys();
while(keyEn.hasMoreElements()) {
Object o = keyEn.nextElement();
System.out.println(o);
}
Enumeration<?> valEn = h.elements();
while(valEn.hasMoreElements()) {
Object o = valEn.nextElement();
System.out.println(o);
}
env = (Context)ctx.lookup("java:comp/env");
h = env.getEnvironment();
Enumeration<?> keys = h.keys();
Enumeration<?> values = h.elements();
System.out.println("Keys:");
while(keys.hasMoreElements()) {
System.out.println(keys.nextElement());
}
System.out.println("Values:");
while(values.hasMoreElements()) {
System.out.println(values.nextElement());
}
Collection<?> col = h.values();
for(Object o : col) {
System.out.println(o);
}
DataSource dataSource = (DataSource)env.lookup("jdbc/twittersearchdb");
Connection conn = dataSource.getConnection();
if(conn instanceof Connection) {
System.out.println("Have a connection from the pool");
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
This gives me output of:
java.naming.factory.initial
java.naming.factory.url.pkgs
org.apache.naming.java.javaURLContextFactory
org.apache.naming
Have a connection from the pool
Keys:
Values:
Have a connection from the pool
What I don't understand
I have got the InitialContext object which, as I understand it, I should be able to get a Hashtable from with keys and values of all the bindings for that context. As the first four lines of the output show, there were only two bindings.Yet I am able to use ctx.lookup("java:comp/env") to get another context that has bindings for Resources for my webapp. There was no "java:comp/env" in the keys from the test output from the InitialContext object. Where did that come from?
Also as you can see I tried to printout the keys and values from the java:comp/env context and got no output and yet I am able to use env.lookup("jdbc/twittersearchdb") which gets me the DataSource that I have specified in my context.xml. Why do I have no output for the bindings for the "java:comp/env" context?
Can I just confirm that as I have specified a Resource element in my context.xml, the container is creating a DataSource onject on deployment of the webapp and the whole Context / InitialContext thing is just a way of using JNDI to access the DataSource object? And if that's the case, why is JNDI used when it seems easier to me to create a DataSource in an implementation of ServletContextListener and have the datasource as a ServletContext attribute?
Does the DataSource object actually manage the ConnectionPool or is that the Container and so is the DataSource object just a way of describing the connection?
How do we access the container directly? What is the object that acctually represents the container? Is it ServletContext? I'm just trying to find out what the container can do for me.
Apologies for the length of this post. I really want to clear up these issues because I'm sure all this stuff is used in every webapp so I need to have it sorted.
Many thanks in advance
Joe