To use push method , we use the famous JS object EventSource :
This is the client part which is easy to develop it :
var eventSource = new EventSource("controller/action");
eventSource.onmessage = function(event) {
document.getElementById('foo').innerHTML = event.data;
};
However , i get difficulty to configure push method in server part(Grails) .
When , i peek in JEE app developped by Servlets , i can see that the server part is as following :
http://viralpatel.net/blogs/html5-server-sent-events-java-servlets-example/
public class TestServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//content type must be set to text/event-stream
response.setContentType("text/event-stream");
//encoding must be set to UTF-8
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
for(int i=0; i<10; i++) {
writer.write("data: "+ System.currentTimeMillis() +"\n\n");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
writer.close();
}
}
To convert this servlet to action controller :
def push(){
render(contentType:'text/event-stream',characterEncoding:'UTF-8'){
[dt:"data: "+ System.currentTimeMillis() +"\n\n"]
}
}
and in client part :
var eventSource = new EventSource(baseURL+"external/push");
eventSource.onmessage=function(e){
console.log(e.data.dt);
}
Unfortunately, i don't get any result at console .
I'm not sure if the render method will handle 'text/event-stream' correctly, but you have direct access to the response object in the action:
def push() {
//content type must be set to text/event-stream
response.setContentType("text/event-stream");
//encoding must be set to UTF-8
response.setCharacterEncoding("UTF-8");
for(int i=0; i<10; i++) {
response.outputStream.write("data: "+ System.currentTimeMillis() +"\n\n");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
response.outputStream.flush()
}
def push = {
response.setContentType("text/event-stream");
for(def i=0;i<10;i++) {
render("data $i: ${System.currentTimeMillis()}\n");
sleep(1000)
}
}
Related
I have a .Net core console application, that uses Confluent.Kafka.
I build a consumer for consuming messages from specific topic.
the app is intended to run a few times every-day, consume the messages on the specified topic and process them.
It took me a while to understand the consumer's vehavior, but the it will consume messages only if its groupId is a one that was never in use before.
Every time I change the consumer's groupId - the comsumer will fetch the messages in the subscribed topic. But on the next runs - with same groupId - the consumer.Consume returns null.
This behvior seems rlated to rebalance between consumers on same group. But I don't understand why - since the consumer should exist only throughout the application liftime. Before leaving the app, I call to consumer.close() and consumer.Dispose(). These should destoy the consumer, so that on the next run, when I create the consumer, again it will be the first and single consumer on the specified groupId. But as I said, this is not what happens in fact.
I know there are messages on the topic - I check it via command-line. And I also made sure the topic has only 1 partition.
The most weird thing is, that I have another .net core console app, which does the same process - and with no issue at all.
I attach the codes of the 2 apps.
Working app - always consuming:
class Program
{
...
static void Main(string[] args)
{
if (args.Length != 2)
{
Console.WriteLine("Please provide topic name to read and SMTP topic name");
}
else
{
var services = new ServiceCollection();
services.AddSingleton<ConsumerConfig, ConsumerConfig>();
services.AddSingleton<ProducerConfig, ProducerConfig>();
var serviceProvider = services.BuildServiceProvider();
var cConfig = serviceProvider.GetService<ConsumerConfig>();
var pConfig = serviceProvider.GetService<ProducerConfig>();
cConfig.BootstrapServers = Environment.GetEnvironmentVariable("consumer_bootstrap_servers");
cConfig.GroupId = "confluence-consumer";
cConfig.EnableAutoCommit = true;
cConfig.StatisticsIntervalMs = 5000;
cConfig.SessionTimeoutMs = 6000;
cConfig.AutoOffsetReset = AutoOffsetReset.Earliest;
cConfig.EnablePartitionEof = true;
pConfig.BootstrapServers = Environment.GetEnvironmentVariable("producer_bootstrap_servers");
var consumer = new ConsumerHelper(cConfig, args[0]);
messages = new Dictionary<string, Dictionary<string, UserMsg>>();
var result = consumer.ReadMessage();
while (result != null && !result.IsPartitionEOF)
{
Console.WriteLine($"Current consumed msg-json: {result.Message.Value}");
...
result = consumer.ReadMessage();
}
consumer.Close();
Console.WriteLine($"Done consuming messages from topic {args[0]}");
}
}
class ConsumerHelper.cs
namespace AggregateMailing
{
using System;
using Confluent.Kafka;
public class ConsumerHelper
{
private string _topicName;
private ConsumerConfig _consumerConfig;
private IConsumer<string, string> _consumer;
public ConsumerHelper(ConsumerConfig consumerConfig, string topicName)
{
try
{
_topicName = topicName;
_consumerConfig = consumerConfig;
var builder = new ConsumerBuilder<string, string>(_consumerConfig);
_consumer = builder.Build();
_consumer.Subscribe(_topicName);
}
catch (System.Exception exc)
{
Console.WriteLine($"Error on ConsumerHelper: {exc.ToString()}");
}
}
public ConsumeResult<string, string> ReadMessage()
{
Console.WriteLine("ReadMessage: start");
try
{
return _consumer.Consume();
}
catch (System.Exception exc)
{
Console.WriteLine($"Error on ReadMessage: {exc.ToString()}");
return null;
}
}
public void Close()
{
Console.WriteLine("Close: start");
try
{
_consumer.Close();
_consumer.Dispose();
}
catch (System.Exception exc)
{
Console.WriteLine($"Error on Close: {exc.ToString()}");
}
}
}
}
Not working app - consuming only on first run after changing consumer groupId to one never in use:
class Program.cs
class Program
{
private static SmtpClient smtpClient;
private static Random random = new Random();
static void Main(string[] args)
{
try
{
var services = new ServiceCollection();
services.AddSingleton<ConsumerConfig, ConsumerConfig>();
services.AddSingleton<SmtpClient>(new SmtpClient("smtp.gmail.com"));
var serviceProvider = services.BuildServiceProvider();
var cConfig = serviceProvider.GetService<ConsumerConfig>();
cConfig.BootstrapServers = Environment.GetEnvironmentVariable("consumer_bootstrap_servers");
cConfig.GroupId = "smtp-consumer";
cConfig.EnableAutoCommit = true;
cConfig.StatisticsIntervalMs = 5000;
cConfig.SessionTimeoutMs = 6000;
cConfig.AutoOffsetReset = AutoOffsetReset.Earliest;
cConfig.EnablePartitionEof = true;
var consumer = new ConsumerHelper(cConfig, args[0]);
...
var result = consumer.ReadMessage();
while (result != null && !result.IsPartitionEOF)
{
Console.WriteLine($"current consumed message: {result.Message.Value}");
var msg = JsonConvert.DeserializeObject<EmailMsg>(result.Message.Value);
result = consumer.ReadMessage();
}
Console.WriteLine("Done sending emails consumed from SMTP topic");
consumer.Close();
}
catch (System.Exception exc)
{
Console.WriteLine($"Error on Main: {exc.ToString()}");
}
}
class ConsumerHelper.cs
using Confluent.Kafka;
using System;
using System.Collections.Generic;
namespace Mailer
{
public class ConsumerHelper
{
private string _topicName;
private ConsumerConfig _consumerConfig;
private IConsumer<string, string> _consumer;
public ConsumerHelper(ConsumerConfig consumerConfig, string topicName)
{
try
{
_topicName = topicName;
_consumerConfig = consumerConfig;
var builder = new ConsumerBuilder<string, string> (_consumerConfig);
_consumer = builder.Build();
_consumer.Subscribe(_topicName);
//_consumer.Assign(new TopicPartition(_topicName, 0));
}
catch (System.Exception exc)
{
Console.WriteLine($"Error on ConsumerHelper: {exc.ToString()}");
}
}
public ConsumeResult<string, string> ReadMessage()
{
Console.WriteLine("ConsumeResult: start");
try
{
return _consumer.Consume();
}
catch (System.Exception exc)
{
Console.WriteLine($"Error on ConsumeResult: {exc.ToString()}");
return null;
}
}
public void Close()
{
Console.WriteLine("Close: start");
try
{
_consumer.Close();
_consumer.Dispose();
}
catch (System.Exception exc)
{
Console.WriteLine($"Error on Close: {exc.ToString()}");
}
Console.WriteLine("Close: end");
}
}
}
In a REST service adding a circuit breaker with hystrix, I could do the following:
#HystrixCommand(fallbackMethod = "getBackupResult")
#GetMapping(value = "/result")
public ResponseEntity<ResultDto> getResult(#RequestParam("request") String someRequest) {
ResultDto resultDto = service.parserRequest(someRequest);
return new ResponseEntity<>(resultDto, HttpStatus.OK);
}
public ResponseEntity<ResultDto> getBackupResult(#RequestParam("request") String someRequest) {
ResultDto resultDto = new ResultDto();
return new ResponseEntity<>(resultDto, HttpStatus.OK);
}
Is there something similar I can do for the gRPC call?
public void parseRequest(ParseRequest request, StreamObserver<ParseResponse> responseObserver) {
try {
ParseResponse parseResponse = service.parseRequest(request.getSomeRequest());
responseObserver.onNext(parseResponse);
responseObserver.onCompleted();
} catch (Exception e) {
logger.error("Failed to execute parse request.", e);
responseObserver.onError(new StatusException(Status.INTERNAL));
}
}
I solved my problem by implementing the circuit-breaker on my client. I used the sentinel library
To react on exceptions ratio for example I added this rule:
private static final String KEY = "callGRPC";
private void callGRPC(List<String> userAgents) {
initDegradeRule();
ManagedChannel channel = ManagedChannelBuilder.forAddress(grpcHost, grpcPort).usePlaintext()
.build();
for (String userAgent : userAgents) {
Entry entry = null;
try {
entry = SphU.entry(KEY);
UserAgentServiceGrpc.UserAgentServiceBlockingStub stub
= UserAgentServiceGrpc.newBlockingStub(channel);
UserAgentParseRequest request = UserAgentRequest.newBuilder().setUserAgent(userAgent).build();
UserAgentResponse userAgentResponse = stub.getUserAgentDetails(request);
} catch (BlockException e) {
logger.error("Circuit-breaker is on and the call has been blocked");
} catch (Throwable t) {
logger.error("Exception was thrown", t);
} finally {
if (entry != null) {
entry.exit();
}
}
}
channel.shutdown();
}
private void initDegradeRule() {
List<DegradeRule> rules = new ArrayList<DegradeRule>();
DegradeRule rule = new DegradeRule();
rule.setResource(KEY);
rule.setCount(0.5);
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
rule.setTimeWindow(60);
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
So I'm tying to develop a very simple TeamCity Plugin. The Plugin is just suppose to take user input and display it. My code seems to work on my local Tomcat server but when i try to integrate it into TeamCity via the Basecontroller class from jetbrains there seems to be no output. This is my code
public class AppServer extends BaseController{
private PluginDescriptor myDescriptor;
public AppServer (WebControllerManager manager, PluginDescriptor descriptor) {
manager.registerController("/demoPlugin.html",this);
myDescriptor=descriptor;
}
public void doGet(HttpServletRequest request, HttpServletResponse responseone)throws IOException, ServletException {
try {
responseone.setContentType("text/html; charset=UTF-8");
PrintWriter out = responseone.getWriter();
try {
out.println("<!DOCTYPE html>");
out.println("<html><head>");
out.println("<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>");
out.println("<title>Echo Servlet</title></head>");
out.println("<body><h2>You have enter</h2>");
String requirement = request.getParameter("requirement");
if (requirement == null || (requirement = htmlFilter(requirement.trim())).length() == 0) {
out.println("<p>Agent Requirement: Not specified</p>");
}
else {
out.println("<p>Agent Requirement: " + requirement + "</p>");
}
out.println("<a href='form_input.html'>BACK</a>");
out.println("</body></html>");
}
finally{
out.close();
}
}
finally{
return;
}
}
public void doPost(HttpServletRequest request, HttpServletResponse responseone)
throws IOException, ServletException {
doGet(request, responseone);
}
private static String htmlFilter(String message) {
if (message == null) return null;
int len = message.length();
StringBuffer result = new StringBuffer(len + 20);
char aChar;
for (int i = 0; i < len; ++i) {
aChar = message.charAt(i);
switch (aChar) {
case '<': result.append("<"); break;
case '>': result.append(">"); break;
case '&': result.append("&"); break;
case '"': result.append("""); break;
default: result.append(aChar);
}
}
return (result.toString());
}
protected ModelAndView doHandle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) throws Exception {
return new ModelAndView(myDescriptor.getPluginResourcesPath("Hello.jsp"));
}
}
I honestly don't know what I'm doing and I cannot seem to find any good tutorials regarding Basecontroller class from jetbrains. Please help.
I am receiving strings from my server that I want to append into a Textarea on the client side (Think chat window). Problem is, when I receive the string, the client freezes.
insertUserNameButton.setOnAction((event) -> {
userName=userNameField.getText();
try {
connect();
} catch (IOException e) {
e.printStackTrace();
}
});
public Client() {
userInput.setOnAction((event) -> {
out.println(userInput.getText());
userInput.setText("");
});
}
private void connect() throws IOException {
String serverAddress = hostName;
Socket socket = new Socket(serverAddress, portNumber);
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
while (true) {
String line = in.readLine();
if (line.startsWith("SUBMITNAME")) {
out.println(userName);
} else if (line.startsWith("MESSAGE")) {
Platform.runLater(()->serverOutput.appendText(line.substring(8) + "\n"));
} else if (line.startsWith("QUESTION")) {
Platform.runLater(()->serverOutput.appendText(line.substring(8) + "\n"));
} else if (line.startsWith("CORRECTANSWER")) {
Platform.runLater(()->serverOutput.appendText(line.substring(14) + "\n"));
}
}
}
public static void main(String[] args) {
launch(args);
}
I have done some research and it seems that using Platform.runLater on each append should fix the problem. It doesn't for me.
Anyone has an idea of what it can be caused by? Thank you!
You are calling connect() on the FX Application Thread. Since it blocks indefinitely via the
while(true) {
String line = in.readLine();
// ...
}
construct, you block the FX Application Thread and prevent it from doing any of its usual work (rendering the UI, responding to user events, etc).
You need to run this on a background thread. It's best to use a Executor to manage the threads:
private final Executor exec = Executors.newCachedThreadPool(runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t ;
});
and then do
insertUserNameButton.setOnAction((event) -> {
userName=userNameField.getText();
exec.execute(() -> {
try {
connect();
} catch (IOException e) {
e.printStackTrace();
}
});
});
I'm trying to access a XML file from client side in GWT. But it looks like the sendRequest method is not getting fired at all.
I'm able to see the xml in the browser. Do I need to do any thing in the server side?
Any help is appreciated.
Here's my code
String xmlurl = "http://localhost:8888/test.xml";
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(xmlurl));
try {
Request request = builder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
System.out.println(exception);
}
public void onResponseReceived(Request request, Response response) {
if (200 == response.getStatusCode()) {
System.out.println(response.getText());
} else {
System.out.println(response.getStatusCode());
}
}
});
} catch (RequestException e) {
System.out.println("exception"+e);
}
I tried the following code too, but have the same problem. The developer tool shows response status as 200 and correct response text. Only, its not working in the code.
String xmlurl = "http://127.0.0.1:8888/test.xml";
httpGetFile(xmlurl, new AsyncCallback<String>() {
public void onFailure(Throwable caught) {
xmlData = "Error";
}
public void onSuccess(String xmlText) {
xmlData = xmlText;
}
});
public static void httpGetFile(final String url, final AsyncCallback<String> callback) {
final RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, url);
rb.setCallback(new RequestCallback() {
public void onResponseReceived(Request request, Response response) {
try {
System.out.println("dafadfdf");
final int responseCode = response.getStatusCode() / 100;
if (url.startsWith("file:/") || (responseCode == 2)) {
callback.onSuccess(response.getText());
} else {
callback.onFailure(new IllegalStateException("HttpError#" + response.getStatusCode() + " - " + response.getStatusText()));
}
} catch (Throwable e) {
callback.onFailure(e);
}
}
public void onError(Request request, Throwable exception) {
callback.onFailure(exception);
}
});
try {
rb.send();
} catch (RequestException e) {
callback.onFailure(e);
}
}
Always Use logging instead of System.out.print statements https://developers.google.com/web-toolkit/doc/latest/DevGuideLogging
Step 1 - Add logging statements to failure, success and try catch statements. Clean up the exception.
Step 2 - "Parsing the XML" should be done inside the "onSuccess" method of the rb callback.
You do not need a RequestBuilder at all to access an XML file. You can use an ExternalTextResource for this:
https://developers.google.com/web-toolkit/doc/latest/DevGuideClientBundle#TextResource