CometD publish a message back to a client - java

I am having a problem in sending back a message to a client. Below is my code
JavaScript
dojox.cometd.publish('/service/getservice', {
userid : _USERID,
});
dojox.cometd.subscribe('/service/getservice', function(
message) {
alert("abc");
alert(message.data.test);
});
Configuration Servlet
bayeux.createIfAbsent("/service/getservice", new ConfigurableServerChannel.Initializer() {
#Override
public void configureChannel(ConfigurableServerChannel channel) {
channel.setPersistent(true);
GetListener channelListner = new GetListener();
channel.addListener(channelListner);
}
});
GetListener class
public class GetListener implements MessageListener {
public boolean onMessage(ServerSession ss, ServerChannel sc) {
SomeClassFunction fun = new SomeClassFunction;
}
}
SomeClassFunction
class SomeClassFunction(){
}
here i am creating a boolean variable
boolean success;
if it is true send a message to client which is in javascript. how to send a message back to client. i have tried this line also.
remote.deliver(getServerSession(), "/service/getservice",
message, null);
but it is giving me an error on remote object and getServerSession method.

In order to reach your goal, you don't need to implement listeners nor to configure channels. You may need to add some configuration at a later stage, for example in order to add authorizers.
This is the code for the ConfigurationServlet, taken from this link:
public class ConfigurationServlet extends GenericServlet
{
public void init() throws ServletException
{
// Grab the Bayeux object
BayeuxServer bayeux = (BayeuxServer)getServletContext().getAttribute(BayeuxServer.ATTRIBUTE);
new EchoService(bayeux);
// Create other services here
// This is also the place where you can configure the Bayeux object
// by adding extensions or specifying a SecurityPolicy
}
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException
{
throw new ServletException();
}
}
This is the code for EchoService class, taken fro this link:
public class EchoService extends AbstractService
{
public EchoService(BayeuxServer bayeuxServer)
{
super(bayeuxServer, "echo");
addService("/echo", "processEcho");
}
public void processEcho(ServerSession remote, Map<String, Object> data)
{
// if you want to echo the message to the client that sent the message
remote.deliver(getServerSession(), "/echo", data, null);
// if you want to send the message to all the subscribers of the "/myChannel" channel
getBayeux().createIfAbsent("/myChannel");
getBayeux().getChannel("/myChannel").publish(getServerSession(), data, null);
}
}

Related

Apache Camel test expectedBodiesReceived check if the body is null?

I have a route that a processor consumes the message and set the body to null.
public class KafkaRedirect implements Processor {
#Override
public void process(final Exchange exchange) throws Exception {
... some logic to send to another party
/**
* This is added to consume the message
*/
exchange.getIn().setBody(null);
}
}
In the test I send message to the route and I want to test if the message is sent and the body is null.
#Test
public void testMyRoute() throws Exception {
final MockEndpoint thirdPartyEndpoin = getMandatoryEndpoint("mock://myRoute", MockEndpoint.class);
context().getRouteDefinition("myRouteId")
.adviceWith(context(), new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
weaveById("myProcessId").after().to(thirdPartyEndpoin);
}
});
startCamelContext();
thirdPartyEndpoin.expectedMessageCount(1);
/* I NEED TO TEST IF THE BODY IS NULL*/
thirdPartyEndpoin.expectedBodiesReceived(null);
template.sendBody(ROUTE_DIRECT_START, "{\"foo\":\"foo\"}");
thirdPartyEndpoin.assertIsSatisfied(TimeUnit.SECONDS.toMillis(EXPECTED_TIMEOUT_SECONDS));
}
Try with something like (typed from top of my head)
thirdPartyEndpoin.message(0).body().isNull();

Routing websocket destination in Spring-boot

Having raw websocket implementation:
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MessageHandler(), "/websocket")
.setAllowedOrigins("*")
.addInterceptors();;
}
}
Handler:
public class MessageHandler extends TextWebSocketHandler {
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
// The WebSocket has been closed
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
String auth = (String) session.getAttributes().get("auth");
System.out.println(auth);
session.sendMessage(new TextMessage("You are now connected to the server. This is the first message."));
}
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage textMessage) throws Exception {
// A message has been received
}
}
The websocket client connect to server ( handshake etc. ) with /websocket url e.g ws://localhost:8080/websocket
However, now that connection is estabilished is there a way how to route messages? Lets say i have app that provides chat and some pop-up functionality ( for simplicity lets say the user sends pop-up message and some pop-up window shows to all of his friends in app ).
Ofcourse i would like to route chat messages to /chat and popup to /popup.
One way how to achieve this is to send json message to server and parse it there e.g:
protected void handleTextMessage(WebSocketSession session, TextMessage textMessage) throws Exception {
String path = getRouteFromJsonMessage(textMessage);
if( ! "".equals(path) && path.equals("chat")
....
if( ! "".equals(path) && path.equals("popup")
....
}
But this seems too slow, parsing json on every message. Is there some other, better way how to achieve routing?
Thanks for help!
Why don't you just register two different MessageHandlers
public class WebSocketConfig implements WebSocketConfigurer {
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new ChatMessageHandler(), "/chat")
.setAllowedOrigins("*")
.addInterceptors()
.addHandler(new PopUpHandler(), "/popup") //etc;
}
}

Change session id in Spring/Stomp/Websocket/Messaging

I am writing an application with spring messaging and stomp and rabbitmq. My application already sends messages from the browser to rabbitmq and back. But i dont want the predefined rabbitmq queue names based on the session id. I want to change the session id on connect. This is what i tried:
#Component
public class MyListener {
private Logger logger = LoggerFactory.getLogger(getClass().getSimpleName());
#EventListener
public void x(SessionConnectEvent event) {
Map<String, Object> headers = event.getMessage().getHeaders();
String id = headers.get("simpSessionId").toString();
logger.info("My current session id is " + id);
headers.put("sessionId", "fred");
}
}
Error is: the map is immutable
You need to update the sessionId before the handshake is done between client <-> server, that is when the headers attributes are defined.
On the other hand, the listener SessionConnectEvent is executed only after the handshake is done.
public class HttpHandshakeInterceptor implements HandshakeInterceptor {
#Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession();
attributes.put("sessionId", "mySessiond");
}
return true;
}
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Exception ex) {
}
}
Also don't forget to register the interceptor on the specific endpoint
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/greeting").addInterceptors(new HttpHandshakeInterceptor());
}
Changing the session ID was not the correct way. I used ServletFilter for Cookie and Security Checks and #SendTo for the correct use of rabbitmq queues.
You can change the session id by creating a Principal for each handshake and then you can
target each connected session with the provided username :
class CustomHandshake extends DefaultHandshakeHandler {
#Override
public Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
Principal principal = request.getPrincipal();
if (principal == null) {
principal = new AnonymousPrincipal();
String uniqueName = UUID.randomUUID().toString();
((AnonymousPrincipal) principal).setName(uniqueName);
}
return principal;
}
}
Do not forget to register the handler as below :
.setHandshakeHandler(new CustomHandshake())
hope this is helpful

Extracting Remote endpoint Object from Spring websocket session

In javax websockets we can use something like the follows
Session.getAsyncRemote().sendText(String text)
Session.getBasicRemote().sendText();
How can we send an asynchronous messages using spring websocket.
From WebSocketSession of spring webscockets can we extract RemoteEndPoint and send an async messages
PS Note: I am using Basic Spring websockets...
The configuration and code is as follows:
#Configuration
#EnableWebMvc
#EnableAspectJAutoProxy
#EnableWebSocket
public class WebMVCConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
private static final String ENDPOINT_URL = "/echo";
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(socketHandler(), ENDPOINT_URL).setAllowedOrigins("*");
}
#Bean
public WebSocketHandler socketHandler() {
return new WebSocketTestHandler();
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public DefaultHandshakeHandler handshakeHandler() {
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
policy.setInputBufferSize(8192);
policy.setIdleTimeout(600000);
return new DefaultHandshakeHandler(new JettyRequestUpgradeStrategy(new WebSocketServerFactory(policy)));
}
public class SpringMVCInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { ApplicationConfig.class, RabbitMQConfig.class, RabbitConnectionFactory.class,
WebPropertyPlaceHolderConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
}
#Configuration
public class WebSocketTestHandler extends TextWebSocketHandler {
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
log.info("Connection is established to Server....:: Session Open : {}", session.isOpen());
}
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
}
#Override
public void afterConnectionClosed(WebSocketSession curSession, CloseStatus status) throws Exception {
}
}
So inside handleTextMessage(WebSocketSession session,TextMessage message) {
Inside this method am creating multiple threads And sending same session Object and some other parameters..Inside each thread am not modifying any session object related parameters but am trying to execute
TextMessage socketMessage = new TextMessage(message);
session.sendMessage(socketMessage);
}
So each thread is trying to send messages using same session Object..But am facing the following error
java.lang.IllegalStateException: Blocking message pending 10000 for BLOCKING
at org.eclipse.jetty.websocket.common.WebSocketRemoteEndpoint.lockMsg(WebSocketRemoteEndpoint.java:130) ~[websocket-common-9.3.8.v20160314.jar:9.3.8.v20160314]
at org.eclipse.jetty.websocket.common.WebSocketRemoteEndpoint.sendString(WebSocketRemoteEndpoint.java:379) ~[websocket-common-9.3.8.v20160314.jar:9.3.8.v20160314]
at org.springframework.web.socket.adapter.jetty.JettyWebSocketSession.sendTextMessage(JettyWebSocketSession.java:188) ~[spring-websocket-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.web.socket.adapter.AbstractWebSocketSession.sendMessage(AbstractWebSocketSession.java:105) ~[spring-websocket-4.2.4.RELEASE.jar:4.2.4.RELEASE]
So is it possible to send asynchronous messages using spring websockets?
If yes please let me know what configuration changes are required in the above code..Or Can we extract the core AsyncRemoteEndPoint and BasicRemoteEndpoint from spring Websocket Session and can we send asynchronous messages..or if not both the above cases ..move the code to common place and put synchonized(sessionObject)
{
sendmessage
}..Sorry if the framing of question is not clear or already a duplicate question
Please note I am not using any Stomp client or anyother features over spring websocket..Am using plain spring websockets..And is it possible to do without using Future(java feature)(If yes..it would be better)?
I used ConcurrentWebSocketSessionDecorator on the session.
according to:
https://jira.spring.io/browse/SPR-13602
The decorator "enforces sending messages one at a time with a send buffer and send time limit per session. That helps quite a bit to limit the impact of slow clients"

Restlet Client :: how to add filters?

I suffering of a lack of documentation on the use of Restlet at the client side.
I am getting a resource on server via a ClientResource:
new ClientResource(url).get();
But the server can return an ETag header. To handle this I want to save the ETag when returned and send it back to the server when using the same url.
Currently I am doing it like this:
ClientResource clientResource = new ClientResource(url);
addEtag(url, clientResource); // add the cached ETag to the query if any
clientResource.get();
saveEtag(url, clientResource); // cache the ETag if any
I would like to do this using the Restlet framework. I am searching for days wihtout understanding the missing link.
I can extend an application, overwrite the createOutboundRoot() method and return a filter:
public class RestLetClient extends Application {
private Client client;
// instantiation of the client and other things here
#Override
public Restlet createOutboundRoot() {
return new Filter(getContext(), client){
#Override
protected int beforeHandle(Request request, Response response) {
addEtag(request);
return super.doHandle(request, response);
}
#Override
protected void afterHandle(Request request, Response response) {
saveEtag(request, reponse);
return super.afterHandle(request, response);
}
};
}
}
BUT how can I use this filtering around the Restlet client from my business code?
EDIT
The best I could get to work until now is this:
Request request = new Request(Method.GET, uri);
//the filter created in original post
filter.handle(request).getEntity();
This works but it is not integrated in the framework. What I am achieving to do is at the client side what is only documented for the server side. On the server you would do:
public class ServerApplication extends Application {
#Override
public Restlet createInboundRoot() {
Router router = new Router(getContext());
router.attach(GET_URL, GetResource.class);
return router;
}
}
and then start the server. The application will the be triggered on the reception of a GET request on the url.
What is the equivalent on the client side? How can I trigger a Client Application? If I have an Application running at the client side I can nicely add filters where they belong in a REST application
EDIT 2
When trying to run my client within an Application I get the error: The filter org.restlet.engine.application.RangeFilter#f372a7a was executed without a next Restlet attached to it.
Here is how I am getting the error. I have a class extending Application that is called from a JUnit test:
public class RestLetClient extends Application {
private final Client client;
Logger logger = LoggerFactory.getLogger(getClass());
public RestLetClient() {
this.client = new Client(Protocol.HTTP);
}
public Representation get(final String uri) throws Exception {
Request request = new Request(Method.GET, uri);
Response response = handle(request);
return response.getEntity();
}
#Override
public Restlet createOutboundRoot() {
return new Filter(getContext(), client) {
#Override
protected int beforeHandle(Request request, Response response) {
addEtagFilter(request);
return super.beforeHandle(request, response);
}
#Override
protected void afterHandle(Request request, Response response) {
saveEtagFilter(request, response);
super.afterHandle(request, response);
}
};
}
private void saveEtagFilter(Request request, Response response) {
logger.debug("saving etag");
}
private void addEtagFilter(Request request) {
logger.debug("adding etag");
}
}
and the unit with a single test method:
public class RestLetClientTest {
public static final String URL = "http://localhost:8123/resource";
private RestLetClient instance;
private Server server;
#Before
public void setUp() throws Exception {
server = new Server(Protocol.HTTP, 8123, new TestApplication());
server.start();
instance = new RestLetClient();
instance.start();
}
#After
public void tearDown() throws Exception {
instance.stop();
}
#Test
public void testGet() throws Exception {
Representation representation = instance.get(URL);
System.out.println(representation.getText());
}
private class TestApplication extends Application {
#Override
public Restlet createInboundRoot() {
return new Router().attach(RestLetClientTest.URL, GetResource.class);
}
}
private class GetResource extends ServerResource {
#Get
public Representation getResource() {
return new StringRepresentation("hello world");
}
}
}
What am I doing wrong?
I had a much nicer answer from a colleague. I post it here for the documentation.
The solution is to use a ClientResource, a Filter and a Client.
The Filter becomes the next() of the ClientResource and the Client the next() of the Filter.
public class ETagFilter extends Filter {
#Override
protected int beforeHandle(Request request, Response response) {
addEtag(request);
return super.beforeHandle(request, response);
}
#Override
protected void afterHandle(Request request, Response response) {
saveEtag(request, reponse);
super.afterHandle(request, response);
}
}
public class RestLetClient extends Application {
public Representation get(final String uri) throws Exception {
Client client = new Client(Protocol.HTTP);
ETagFilter eTagFilter = new ETagFilter();
clientResource = new ClientResource(uri);
clientResource.setNext(eTagFilter);
eTagFilter.setNext(client);
return clientResource.get(halMediaType);
}
}
For info. In my OP I was trying to transform code meant for server side into client side. That approach was wrong. My colleague pointed that the approach is much more like the use Apache HttpClient for similar needs
To have a client working you need to take the Application out of the picture since it is Server oriented according to the javadoc.
What you need is a Component, a ClientRouter and a custom ClientRoute.
Component manage connectors. A Restlet Client is a Connector.
ClientRouter dispatches to client connectors.
ClientRoute extends Filter allowing to add filters around your client handeling.
My solution:
The Component
public class RestLetComponent extends Component {
public RestLetComponent(Client client) {
getClients().add(client);
}
}
The ClientRouter
public class RestLetClientRouter extends ClientRouter {
public RestLetClientRouter(final Client client) {
super(new RestLetComponent(client));
ClientRoute clientRoute = new RestLetClientRoute(this, client);
//forcing to use only our custom route
getRoutes().clear();
getRoutes().add(clientRoute);
}
public Representation get(final String uri) throws Exception {
Request request = new Request(Method.GET, uri);
Response response = handle(request);
return response.getEntity();
}
}
And the custom ClientRoute that will add the filters
public class RestLetClientRoute extends ClientRoute {
Logger logger = LoggerFactory.getLogger(getClass());
public RestLetClientRoute(Router router, Client client) {
super(router, client);
}
//the filters
#Override
protected int beforeHandle(Request request, Response response) {
addEtagFilter(request);
return super.beforeHandle(request, response);
}
#Override
protected int doHandle(Request request, Response response) {
logger.debug("handling request: " + request.getMethod() + " - " + request.getResourceRef());
return super.doHandle(request, response);
}
#Override
protected void afterHandle(Request request, Response response) {
saveEtagFilter(request, response);
super.afterHandle(request, response);
}
private void saveEtagFilter(Request request, Response response) {
logger.debug("saving etag");
}
private void addEtagFilter(Request request) {
logger.debug("adding etag");
}
}
And last but not least, I apologize to the Restlet authors, the documentation is there. I was reading the Restlet in Action book but the answer is in the very well documented javadoc.

Categories

Resources