I have created a spring boot Messaging endpoint and need to create an android chat app and am wondering how I can manage to call those endpoints using okttp Websocket client which does not seem to have a way to add api endpoints like this javascript code.
And here is my spring boot endpoints
#Configuration
#EnableWebSocketMessageBroker
public class WebMessageConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker( "/user");
config.setApplicationDestinationPrefixes("/app");
config.setUserDestinationPrefix("/user");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry
.addEndpoint("/ws")
.withSockJS()
.setAllowedOrigins("*");
}
}
And here is my OkHttp client code
public class StompWs {
private String SERVER_PATH="ws://mydomain.com:8443/MyContex/ws";
public static void main(String[] args) {
try {
new StompWs().run();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private WebSocket webSocket;
public void run() throws Exception {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(SERVER_PATH).build();
webSocket = client.newWebSocket(request, new SocketListener());
}
private String getData()
{
MessageModel message=new MessageModel();
message.setMessage("Hello");
message.setRecipientId("1");
message.setSenderId("2");
return new Gson().toJson(message);
}
private class SocketListener extends WebSocketListener {
#Override
public void onOpen(WebSocket webSocket, Response response) {
super.onOpen(webSocket, response);
try {
webSocket.send(getData());
/**I need equivalent of this
stompClient.subscribe(
"/user/1/queue/messages",// I need java code to do this
onMessageReceived
*/
}
catch(Exception e)
{
e.printStackTrace();
}
System.out.println("succesfully connected:"+response.toString());//this message execute well
}
#Override
public void onMessage(WebSocket webSocket, String text) {
super.onMessage(webSocket, text);
System.out.println("on message:"+text);
}
#Override
public void onFailure(WebSocket webSocket, Throwable t,
Response response) {
// TODO Auto-generated method stub
super.onFailure(webSocket, t, response);
System.out.println("on message:"+t.toString());
}
}
}
Related
I have the following java code that I'd like to use in an android app to query an api for continuous lat/lng changes of a device that is running a client app, I want to track the device. I believe the WebSocketCall method I'm attempting to use is deprecated. From what I can tell, there's a problem with how I'm trying to use the webSocket call to create the retrofit client and enqueue the data from the WebSocketListner into retrofit. I've researched several WebSocketListener examples and being a total n00b, I haven't been able to figure out the code. My idea is to keep the connection open to the api via WebSocket and process the data response using retrofit. Any assistance would be greatly appreciated.
private WebSocketCall webSocket;
private void createWebSocket() {
final MainApplication application = (MainApplication) getActivity().getApplication();
application.getServiceAsync(new MainApplication.GetServiceCallback() {
#Override
public void onServiceReady(final OkHttpClient client, final Retrofit retrofit, WebService service) {
User user = application.getUser();
map.moveCamera(CameraUpdateFactory.newLatLngZoom(
new LatLng(user.getLatitude(), user.getLongitude()), user.getZoom()));
service.getDevices().enqueue(new WebServiceCallback<List<Device>>(getContext()) {
#Override
public void onSuccess(retrofit2.Response<List<Device>> response) {
for (Device device : response.body()) {
if (device != null) {
devices.put(device.getId(), device);
}
}
Request request = new Request.Builder().url(retrofit.baseUrl().url().toString() + "api/socket").build();
webSocket = WebSocketCall.create(client, request);
webSocket.enqueue(new WebSocketListener() {
#Override
public void onOpen(WebSocket webSocket, Response response) {
}
#Override
public void onFailure(IOException e, Response response) {
reconnectWebSocket();
}
#Override
public void onMessage(ResponseBody message) throws IOException {
final String data = message.string();
handler.post(new Runnable() {
#Override
public void run() {
try {
handleMessage(data);
} catch (IOException e) {
Log.w(MainFragment.class.getSimpleName(), e);
}
}
});
}
#Override
public void onClose(int code, String reason) {
reconnectWebSocket();
}
});
}
});
}
#Override
public boolean onFailure() {
return false;
}
});
}
So because I'm a total n00b it took some time and a lot of questions to figure this out. Maybe it'll help someone else in the future.
private WebSocket webSocket;
private void createWebSocket() {
final MainApplication application = (MainApplication) getActivity().getApplication();
application.getServiceAsync(new MainApplication.GetServiceCallback() {
#Override
public void onServiceReady(final OkHttpClient client, final Retrofit retrofit, WebService service) {
User user = application.getUser();
map.moveCamera(CameraUpdateFactory.newLatLngZoom(
new LatLng(user.getLatitude(), user.getLongitude()), user.getZoom()));
service.getDevices().enqueue(new WebServiceCallback<List<Device>>(getContext()) {
#Override
public void onSuccess(retrofit2.Response<List<Device>> response) {
for (Device device : response.body()) {
if (device != null) {
devices.put(device.getId(), device);
}
}
Request request = new Request.Builder().url(retrofit.baseUrl().url().toString() + "api/socket").build();
Log.e("WebSockets", "Headers: " + request.headers().toString());
WebSocketListener webSocketListener = new WebSocketListener() {
private static final int NORMAL_CLOSURE_STATUS = 1000;
#Override
public void onOpen(WebSocket webSocket, Response response) {
webSocket.send("{Auth-Token:secret-api-token-here}");
Log.e("WebSockets", "Connection accepted!");
}
#Override
public void onFailure(#NotNull WebSocket webSocket, #NotNull Throwable t, #Nullable Response response) {
reconnectWebSocket();
}
#Override
public void onMessage(#NotNull WebSocket webSocket, #NotNull String text) {
final String data = text;
Log.e("WebSockets", "Receiving : " + text);
handler.post(new Runnable() {
#Override
public void run() {
try {
handleMessage(data);
} catch (IOException e) {
Log.w(MainFragment.class.getSimpleName(), e);
}
}
});
}
#Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
Log.e("WebSockets", "Receiving bytes : " + bytes.hex());
}
#Override
public void onClosing(WebSocket webSocket, int code, String reason) {
webSocket.close(NORMAL_CLOSURE_STATUS, null);
Log.e("WebSockets", "Closing : " + code + " / " + reason);
}
#Override
public void onClosed(#NotNull WebSocket webSocket, int code, #NotNull String reason) {
reconnectWebSocket();
}
};
webSocket = client.newWebSocket(request, webSocketListener);
}
});
}
#Override
public boolean onFailure() {
return false;
}
});
}
I created a basic selfhosted SignalR server with the following code:
class Program
{
static void Main(string[] args)
{
// This will *ONLY* bind to localhost, if you want to bind to all addresses
// use http://*:8080 to bind to all addresses.
// See http://msdn.microsoft.com/en-us/library/system.net.httplistener.aspx
// for more information.
string url = "http://localhost:8080";
using (WebApp.Start(url))
{
Console.WriteLine("Server running on {0}", url);
Console.ReadLine();
}
}
}
class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
}
public class MyHub : Hub
{
public void Send(string name, string message)
{
Clients.All.addMessage(name, message);
}
}
Which is taken from: https://learn.microsoft.com/en-us/aspnet/signalr/overview/deployment/tutorial-signalr-self-host and works with the Javascript client.
I am now trying to create a Java client and got the following code that is simply supposed to send a message to the server:
String host = "http://localhost:8080";
HubConnection connection = new HubConnection(host);
HubProxy proxy = connection.createHubProxy("MyHub");
connection.start();
try {
System.out.println("Sendng message...");
proxy.invoke( "Send", "Client", "Hello world!" ).get();
System.out.println("Message sent!");
} catch (InterruptedException e) {
System.out.println("err1");
// Handle ...
} catch (ExecutionException e) {
System.out.println("err2");
// Handle ...
}
The problem that im having is that the message is not received by the server, it seems like the code is stuck at the invoke call and doesn't print the Hello world! message. Does someone know what im doing wrong?
hubProxy.invoke("sendMessageByUser", Message, WebApiToken).done(new Action<Void>() {
#Override
public void run(Void aVoid) {
if (aVoid != null)
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(MyApplicationService.this, "Mesaj gönderildi", Toast.LENGTH_SHORT).show();
}
});
}
}).onError(new ErrorCallback() {
#Override
public void onError(final Throwable error) {
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(MyApplicationService.this.getApplicationContext(), "Bir hata oluştu" + error.toString(), Toast.LENGTH_SHORT).show();
}
});
}
});
I am trying to have a fallback if Zuul does not find a service. I have the a ZuulSever with the below code:
#SpringBootApplication
#EnableZuulProxy
#EnableDiscoveryClient
public class ZuulServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulServerApplication.class, args);
}
#Bean
public ZuulFallbackProvider fallBackProvider() {
return new ZuulFallbackProvider() {
#Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
#Override
public HttpHeaders getHeaders() {
return null;
}
#Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("Hello".getBytes());
}
#Override
public String getStatusText() throws IOException {
// TODO Auto-generated method stub
return "Service Down";
}
#Override
public HttpStatus getStatusCode() throws IOException {
// TODO Auto-generated method stub
return HttpStatus.OK;
}
#Override
public int getRawStatusCode() throws IOException {
// TODO Auto-generated method stub
return 200;
}
#Override
public void close() {
// TODO Auto-generated method stub
}
};
}
#Override
public String getRoute() {
// TODO Auto-generated method stub
return "*";
}
};
}
}
When the service in my route is up and running, I am able to get the output. But when I bring down the service in the route, I expected the fallback to kick in. But I still see an error message instead of the fallback message. Why is the fallback not invoked? I am using Dalston Release version.
If you configure Zuul to directly connect to an URL for your route, it will use SimpleHostRoutingFilter, which will (almost) always return a 500 in case of an error. Any FallbackProviders will not kick in.
I used a custom SimpleHostRoutingFilter instead:
public class CustomErrorHostRoutingFilter extends SimpleHostRoutingFilter {
public CustomErrorHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties properties, ApacheHttpClientConnectionManagerFactory connectionManagerFactory, ApacheHttpClientFactory httpClientFactory) {
super(helper, properties, connectionManagerFactory, httpClientFactory);
}
#Override
protected ZuulException handleException(Exception ex) {
if (ex instanceof ConnectTimeoutException) {
return new ZuulException(ex, "Downstream timeout", HttpServletResponse.SC_GATEWAY_TIMEOUT, ex.getMessage());
}
if (ex instanceof IOException) {
return new ZuulException(ex, "Downstream I/O error", HttpServletResponse.SC_SERVICE_UNAVAILABLE, ex.getMessage());
}
return super.handleException(ex);
}
}
Some kind of configuration class is required as well:
#Configuration
#EnableZuulProxy
public class ZuulConfiguration {
#Bean
public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper,
ZuulProperties zuulProperties,
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
ApacheHttpClientFactory httpClientFactory) {
return new CustomErrorHostRoutingFilter(helper, zuulProperties, connectionManagerFactory, httpClientFactory);
}
}
Currently in my application, a message is broadcast each 10 second with spring websockets. This is how the messages are broadcast to users in my spring application.
#Configuration
#EnableWebSocketMessageBroker
#EnableScheduling
#Component
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Autowired
private SimpMessagingTemplate template;
private TaskScheduler scheduler = new ConcurrentTaskScheduler();
public WebSocketConfig() {
System.out.printf(" ---INIT----------");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/simplemessages").withSockJS();
}
// #Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic/", "/queue/");
config.setApplicationDestinationPrefixes("/app");
}
#PostConstruct
private void broadcastTimePeriodically() {
scheduler.scheduleAtFixedRate(new Runnable() {
public void run() {
try{
template.convertAndSend("/topic/simplemessagesresponse", "{shares:true,price:100.00}");
}catch(MessagingException e){
System.err.println("!!!!!! websocket timer error :>"+e.toString());
}
}
}, 10000);
}
#PreDestroy
private void destroyServices(){
}
// #Override
public void configureClientInboundChannel(ChannelRegistration registration) {
}
// #Override
public void configureClientOutboundChannel(ChannelRegistration registration) {
registration.taskExecutor().corePoolSize(4).maxPoolSize(10);
}
//#Override
public boolean configureMessageConverters(List<MessageConverter> arg0) {
// TODO Auto-generated method stub
return true;
}
#Override
public void configureWebSocketTransport(WebSocketTransportRegistration arg0) {
// TODO Auto-generated method stub
}
}
This is how the browser receives,
var socket = new SockJS(desz);
stompClient = Stomp.over(socket);
stompClient.connect('user', 'guest', function(frame) {
stompClient.subscribe("/topic/simplemessagesresponse", function(servermessage) {
var stompResponse = JSON.parse((servermessage.body));
console.log('server msg: '+stompResponse);
});
});
I want to broadcast same message to some users, while another set of users have another message periodically. How I should modify my above code to achieve this ?
You can have this in your scheduler run() method
this.simpMessagingTemplate.convertAndSend("/queue/" + userGroup.geName(),
messageMap.get(userGroup.geName()));
and in the client side you can subscribe to specific url "queue/{groupName}"
stompClient.subscribe("/queue/${groupName}", function(servermessage) {
var stompResponse = JSON.parse((servermessage.body));
console.log('server msg: '+stompResponse);
});
NOTE :(in client example variable 'groupName' is sent to view from controller and accessed using EL in JSP)
I have a bolt that is making an API call (HTTP Get) for every tuple.
to avoid the need to wait for the response, I was looking to use the apache HttpAsyncClient.
after instantiating the client in the bolt's prepare method, the execute method constructs the URL from the tuple and calls sendAsyncGetRequest(url):
private void sendAsyncGetRequest(String url){
httpclient.execute(new HttpGet(url), new FutureCallback<HttpResponse>() {
#Override
public void completed(HttpResponse response) {
LOG.info("Response Code : " + response.getStatusLine());
LOG.debug(response.toString());
}
#Override
public void failed(Exception ex) {
LOG.warn("Async http request failed!", ex);
}
#Override
public void cancelled() {
LOG.warn("Async http request canceled!");
}
});
}
the topology deploys but the Storm UI shows an error:
java.lang.RuntimeException: java.lang.IllegalStateException: Request cannot be executed; I/O reactor status: STOPPED at backtype.storm.utils.DisruptorQueue.consumeBatchToCursor(DisruptorQueue.java:12
I got this to work with no issues.
the key things to note are:
declare the client on the bolt class scope
public class MyRichBolt extends BaseRichBolt {
private CloseableHttpAsyncClient httpclient;
Instantiate and stat the client in the bolt's prepare method
#Override
public final void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
try {
// start the http client
httpclient = HttpAsyncClients.createDefault();
httpclient.start();
// other initialization code ...
} catch (Throwable exception) {
// handle errors
}
}
make the calls in the bolt's execute method
#Override
public final void execute(Tuple tuple) {
// format the request url
String url = ...
sendAsyncGetRequest(url);
}
private void sendAsyncGetRequest(String url){
logger.debug("Async call to URL...");
HttpGet request = new HttpGet(url);
HttpAsyncRequestProducer producer = HttpAsyncMethods.create(request);
AsyncCharConsumer<HttpResponse> consumer = new AsyncCharConsumer<HttpResponse>() {
HttpResponse response;
#Override
protected void onResponseReceived(final HttpResponse response) {
this.response = response;
}
#Override
protected void onCharReceived(final CharBuffer buf, final IOControl ioctrl) throws IOException {
// Do something useful
}
#Override
protected void releaseResources() {
}
#Override
protected HttpResponse buildResult(final HttpContext context) {
return this.response;
}
};
httpclient.execute(producer, consumer, new FutureCallback<HttpResponse>() {
#Override
public void completed(HttpResponse response) {
// do something useful with the response
logger.debug(response.toString());
}
#Override
public void failed(Exception ex) {
logger.warn("!!! Async http request failed!", ex);
}
#Override
public void cancelled() {
logger.warn("Async http request canceled!");
}
});
}
Are you shutting down the client (client.close();) in your main flow before the callback can execute?
The error is saying that the IO path has already been closed. In general, instances of async clients should be re-used for repeated requests and destroyed only when "ALL" requests have been made, e.g. at application shutdown.