Implementing WebSocket on Spring MVC based Server - spring-mvc

I searched a lot about implementing WebSocket/XMPP on Spring MVC based server but couldn't reach to a concrete answer. So here is my requirement
Receive a message from a client (in my case it will be a android/iOS mobile) via WebSocket/XMPP on tomcat server and parse the actual message at server side
Send a message from server app to WebSocket/XMPP client
If somebody could help me to point on some good tutorial or demo code, it would be a great help.

run Tomee 1.5.2
http://openejb.apache.org/downloads.html
activate the ActiveMQ JMS Server. create an OpenEJB configuration.
http://www.mail-archive.com/users#openejb.apache.org/msg04327.html
setup an XMPP ActiveMQ server protocol listener (in the activemq.xml)
in your Spring services configuration, create a Spring JMS listener (Spring ListenerContainer) configuration on the Topic/Queue.
you can use the JmsTemplate to push a message out to the Queue/Topic via ActiveMQ, the XMPP client will receive the message.
Enjoy!
BTW: This is exactly what I am in the middle of setting up right now...still learning.

check this out: www.xchat.io. It was built based on Asynchronous Spring MVC (DefferredResult, you know), XMPP, and jQuery. it's promising.

I am not sure if this is just perfect way to achieve or not, but for now I have found a solution and it would be glad to share it here.
There are two steps that you have to done.
1. Instead of ordinary HTTPServlet sub class, create a sub class of WebSocketServlet and
2. Create a sub class of MessageInbound class and override its required methods.
P.S. : Only latest version of tomcat supports WebSocket (apache tomcat 7.0.42 or higher).
Here is a WebSocket class.
public class WsChatServlet extends WebSocketServlet {
private static final long serialVersionUID = 1456546233L;
#Override
protected StreamInbound createWebSocketInbound(String protocol,
HttpServletRequest request) {
return new IncomingMessageHandler();
}
}
And this is a simple class which can send/receive message (String/binary).
public class IncomingMessageHandler extends MessageInbound {
#Override
public void onOpen(WsOutbound outbound) {
logger.info("Open Client.");
}
#Override
public void onClose(int status) {
logger.info("Close Client.");
}
#Override
public void onTextMessage(CharBuffer cb) throws IOException {
logger.info("Text Message received:" + cb.toString());
}
#Override
public void onBinaryMessage(ByteBuffer bb) throws IOException {
}
public synchronized void sendTextMessage(String message) {
try {
CharBuffer buffer = CharBuffer.wrap(message);
this.getMyoutbound().writeTextMessage(buffer);
this.getMyoutbound().flush();
} catch (IOException e) {
// Handle Exception
}
}
}

Related

How to configure client-side load balancing with grpc-java

I've seen some high-level information about load balancing, but am struggling to put the pieces together. Here's what I've reviewed:
Load Balancing in gRPC (gRPC GitHub)
gRPC Load Balancing (gRPC blog)
gRPC on HTTP/2 Engineering a Robust, High-performance Protocol (gRPC blog)
gRPC client-side load balancing (Microsoft guide)
Java gRPC Custom Client-side load balancing (Stack Overflow)
Obviously the core pieces are a resolver and a load balancer. My use case is that I have several static, known addresses. I simply want to prioritize them as primary, secondary, etc. I believe the pick_first policy will work for this.
What I can't figure out is how to set up a custom NameResolver. I've defined a custom NameResolverProvider:
public class StaticResolverProvider extends NameResolverProvider {
#Value("${tls.enabled}")
private boolean isTlsEnabled;
#Override
protected boolean isAvailable() {
return true;
}
#Override
protected int priority() {
return 10;
}
#Override
public NameResolver newNameResolver(URI targetUri, Args args) {
return new StaticResolver();
}
#Override
public String getDefaultScheme() {
return isTlsEnabled ? "https" : "http";
}
}
and (hopefully) registered it while creating my Channel:
new NameResolverRegistry().register(new StaticResolverProvider());
Finally, here is the (currently unimplemented) NameResolver:
public class StaticResolver extends NameResolver {
#Override
public String getServiceAuthority() {
return null;
}
#Override
public void shutdown() {
}
}
These are the only two methods I see that need to be implemented. Neither of these seem to have anything to do with returning an ordered list of known addresses. The getServiceAuthority() mentions authentication, which confuses me because I don't know what the NameResolver has to do with authentication.
Please advise on what I'm missing. Thanks!
Update
I figured out the Name Resolver piece. First off, registering my resolver with my Channel looked a little different:
NameResolverRegistry.getDefaultRegistry().register(new StaticResolverProvider());
In my NameResolverProvider, I updated the getDefaultScheme() method to return "customScheme", which is the piece that would link it to my channel's call to forTarget().
The final piece was to implement the refresh() method in my NameResolver:
#Override
public void refresh() {
ResolutionResult.Builder resolutionResultBuilder = ResolutionResult.newBuilder();
List<EquivalentAddressGroup> servers = new ArrayList<>();
servers.add(new EquivalentAddressGroup(new InetSocketAddress("localhost", 50055)));
servers.add(new EquivalentAddressGroup(new InetSocketAddress("localhost", 50056)));
resolutionResultBuilder.setAddresses(Collections.unmodifiableList(servers));
listener.onResult(resolutionResultBuilder.build());
}
These are the only two methods I see that need to be implemented.
Those are the abstract ones. But the main one you need to implement is refresh() which is defined as no-op but needs to be overridden in your implementation to do anything useful. You can look at UdsNameResolver to see how refresh() is implemented and follow that pattern.
The getServiceAuthority() mentions authentication
You can ignore that for your use-case.

FeignClients get published as REST endpoints in spring cloud application

I've got REST FeignClient defined in my application:
#FeignClient(name = "gateway", configuration = FeignAuthConfig.class)
public interface AccountsClient extends Accounts {
}
I share endpoint interface between server and client:
#RequestMapping(API_PATH)
public interface Accounts {
#PostMapping(path = "/register",
produces = APPLICATION_JSON_VALUE,
consumes = APPLICATION_JSON_VALUE)
ResponseEntity<?> registerAccount(#RequestBody ManagedPassUserVM managedUserDTO)
throws EmailAlreadyInUseException, UsernameAlreadyInUseException, URISyntaxException;
}
Everythng works fine except that my FeignClient definition in my client application also got registered as independent REST endpoint.
At the moment I try to prevent this behavior using filter which returns 404 status code for FeignClinet client mappings in my client application. However this workeraund seems very inelegant.
Is there another way how to prevent feign clients registering as separate REST endpoints?
It is a known limitation of Spring Cloud's feign support. By adding #RequestMapping to the interface, Spring MVC (not Spring Cloud) assumes you want as an endpoint. #RequestMapping on Feign interfaces is not currently supported.
I've used workaround for this faulty Spring Framework behavior:
#Configuration
#ConditionalOnClass({Feign.class})
public class FeignMappingDefaultConfiguration {
#Bean
public WebMvcRegistrations feignWebRegistrations() {
return new WebMvcRegistrationsAdapter() {
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new FeignFilterRequestMappingHandlerMapping();
}
};
}
private static class FeignFilterRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
#Override
protected boolean isHandler(Class<?> beanType) {
return super.isHandler(beanType) && (AnnotationUtils.findAnnotation(beanType, FeignClient.class) == null);
}
}
}
I found it in SpringCloud issue

Spring Cloud Netflix : Passing host request parameter via RequestInterceptor to FeignClient

I am building a Spring Cloud project (Brixton.M4 with Spring Boot 1.3.1) with Eureka, Zuul and FeignClient where I am trying to add multi tenancy support (Tenants are identified by subdomain : tenant1.myservice.com). To do so, I would like to somehow pass the original subdomain along requests that are forwarded from a service to the other via Feign but I can't seem to be able to find the right way to do it.
What I have is a client that exposes a #RestController which calls a #FeignClient to communicate with my backend which exposes server operations to the client through its own #RestController.
The #FeignClient using same interface as my #RestController on the server :
#FeignClient(name = "product")
public interface ProductService extends IProductService {
}
What I am currently trying to do is set a header in a RequestInterceptor :
#Component
public class MultiTenancyRequestInterceptor implements RequestInterceptor {
private CurrentTenantProvider currentTenantProvider;
#Autowired
public MultiTenancyRequestInterceptor(CurrentTenantProvider currentTenantProvider) {
this.currentTenantProvider = currentTenantProvider;
}
#Override
public void apply(RequestTemplate template) {
try {
template.header("TENANT", currentTenantProvider.getTenant());
} catch (Exception e) {
// "oops"
}
}
}
My provider class is a simple component where I'm trying to inject a request / session scope bean :
#Component
public class CurrentTenantProvider {
#Autowired
private CurrentTenant currentTenant;
//...
}
The bean (I tried both session and request scope) :
#Bean
#Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public CurrentTenant currentTenant() {
return new CurrentTenant();
}
On the server, I use Hibernate multitenant provider that is supposed to catch the header value and use it to define which DB to connect to :
#Autowired
private HttpServletRequest httpRequest;
#Override
public String resolveCurrentTenantIdentifier() {
return httpRequest.getHeader("TENANT");
}
It seems the Feign call to the server is done in another thread and out of the incoming request scope, so i'm not sure how to pass that value along.
It all works fine when I hardcode the tenant value in the RequestInterceptor so I know the rest is working properly.
I have also looked at many other posts about Zuul "X-Forwaded-For" header and cannot find it in the request received on the server. I have also tried adding a ZuulFilter to pass host name to next request but what I see is that original request to the Client is picked up by the ZuulFilter and I can add but not when the Feign request is sent to the backend service even if I map it in zuul (i guess that is intended ?).
I am not really sure what's the next step and would appreciate some suggestions.
Hope that it's of any use for you but we're doing sth similar in Spring-Cloud-Sleuth but we're using a ThreadLocal to pass span between different libraries and approaches (including Feign + Hystrix).
Here is an example with the highlighted line where we retrieve the Span from the thread local: https://github.com/spring-cloud/spring-cloud-sleuth/blob/master/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceFeignClientAutoConfiguration.java#L123

Accessing ServletContext and HttpSession in #OnMessage of a JSR-356 #ServerEndpoint

I need to get the ServletContext from inside a #ServerEndpoint in order to find Spring ApplicationContext and lookup for a Bean.
For the moment my best approach is to bind that bean in the JNDI naming context and lookup it in the Endpoint. Any better solution is welcome.
I'm also looking for a reasonable way to sync servlet's HttpSession with websocket's Session.
The servlet HttpSession is in JSR-356 available by HandshakeRequest#getHttpSession() which is in turn available when a handshake request is made right before #OnOpen of a #ServerEndpoint. The ServletContext is in turn just available via HttpSession#getServletContext(). That's two birds with one stone.
In order to capture the handshake request, implement a ServerEndpointConfig.Configurator and override the modifyHandshake() method. The HandshakeRequest is here available as method argument. You can put the HttpSession into EndpointConfig#getUserProperties(). The EndpointConfig is in turn available as method argument #OnOpen.
Here's a kickoff example of the ServerEndpointConfig.Configurator implementation:
public class ServletAwareConfig extends ServerEndpointConfig.Configurator {
#Override
public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
HttpSession httpSession = (HttpSession) request.getHttpSession();
config.getUserProperties().put("httpSession", httpSession);
}
}
Here's how you can use it, note the configurator attribute of the #ServerEndpoint:
#ServerEndpoint(value="/your_socket", configurator=ServletAwareConfig.class)
public class YourSocket {
private EndpointConfig config;
#OnOpen
public void onOpen(Session websocketSession, EndpointConfig config) {
this.config = config;
}
#OnMessage
public void onMessage(String message) {
HttpSession httpSession = (HttpSession) config.getUserProperties().get("httpSession");
ServletContext servletContext = httpSession.getServletContext();
// ...
}
}
As a design hint, it's the best to keep your #ServerEndpoint fully free of servlet API dependencies. You'd in the modifyHandshake() implementation better immediately extract exactly that information (usually a mutable Javabean) you need from the servlet session or context and put them in the user properties map instead. If you don't do that, then you should keep in mind that a websocket session can live longer than the HTTP session. So when you still carry around HttpSession into the endpoint, then you may run into IllegalStateException when you try to access it while it's being expired.
In case you happen to have CDI (and perhaps JSF) at hands, you may get inspiration from the source code of OmniFaces <o:socket> (links are at very bottom of showcase).
See also:
Real time updates from database using JSF/Java EE
Notify only specific user(s) through WebSockets, when something is modified in the database
Updated code for BalusC's answer, the onOpen method needs to be decorated with #OnOpen. Then there is no need anymore to extend the Endpoint class:
#ServerEndpoint(value="/your_socket", configurator=ServletAwareConfig.class)
public class YourSocket {
private EndpointConfig config;
#OnOpen
public void onOpen(Session websocketSession, EndpointConfig config) {
this.config = config;
}
#OnMessage
public void onMessage(String message) {
HttpSession httpSession = (HttpSession) config.getUserProperties().get("httpSession");
ServletContext servletContext = httpSession.getServletContext();
// ...
}
}
I tried out BalusC's answer on Tomcat (Versions 7.0.56 and 8.0.14). On both containers, the modifyHandshake's request parameter does not contain a HttpSession (and thus no servletContext).
As I needed the servlet context only to access "global" variables (web-application global, that is), I just stored these variables in an ordinary static field of a holder class. This is inelegant, but it worked.
That ooks like a bug in this specific tomcat versions - has anyone out there also seen this?
Somtimes we can't get session with above ServletAwareConfig of BalusC, this is because that the session is still not created. since we are not seek for session but servletContext, in tomcat we can do as below:
public static class ServletAwareConfig extends ServerEndpointConfig.Configurator {
#Override
public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
try {
Field reqfld = request.getClass().getDeclaredField("request");
reqfld.setAccessible(true);
HttpServletRequest req = (HttpServletRequest) reqfld.get(request);
ServletContext ctxt = req.getServletContext();
Map<String, Object> up = config.getUserProperties();
up.put("servletContext", ctxt);
} catch (NoSuchFieldException e) {
} catch (SecurityException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
}
}
if we want init session immediately, we can call request.getSession().
Ref: Websocket - httpSession returns null

Wicket and responding with "not HTML" to requests

I'm sure this has been answered somewhere else - but I don't know where
I need to respond to HTTP requests from a partner, in our wicket website. The partner expected the response body to say "OK" or anything else in the case of an error
Is there a "nice" way to do this? ... or am I going to be stuck adding a servlet to my (previously) pretty Wicket application?
You can use resources for that:
class OkResource implements IResource {
#Override
public void respond(Attributes attributes) {
WebResponse resp = (WebResponse) attributes.getResponse();
resp.setContentType("text/plain");
resp.write("OK");
}
}
And register it in your Application class
#Override
protected void init() {
super.init();
getSharedResources().add("confirm", new OkResource());
mountResource("confirm", new SharedResourceReference("confirm"));
}
so that it can be accessed through something like http://host/app/confirm.
Just observe that here you registering a single instance of the resource, so it must be thread-safe, since multiple requests can call it simultaneously.
[EDIT]
In Wicket 1.4:
class OkResource extends Resource {
#Override
public IResourceStream getResourceStream() {
return new StringResourceStream("ok", "text/plain");
}
}
#Override
protected void init() {
super.init();
getSharedResources().add("confirm", new OkResource());
mountSharedResource("confirm", "confirm");
}

Resources