Spring Websockets not working under custom application context path - java

I have application which uses Spring 4.3.5 and spring mvc - apache tiles .
I wrote chat according to this article https://spring.io/guides/gs/messaging-stomp-websocket/
Everything is working correctly, if my whole application context path is root so for example : http://example.com/ I recieve following frames in websocket
["SUBSCRIBE\nid:sub-0\ndestination:/chat-messages/TST\n\n\u0000"]
["SEND\ndestination:/chat/message/TST\ncontent-length:52\n\n{\"message\":\"\",\"username\":\"USER\",\"event\":\"ONLINE\"}\u0000"]
["MESSAGE\ndestination:/chat-messages/TST\ncontent-type:application/json;charset=UTF-8\nsubscription:sub-0\nmessage-id:x1jpjyes-1\ncontent-length:230\n\n{..SOME JSON CONTENT....}\u0000"]
Problem is that it stops working, If I add some app context ( and I need to do so on my server)
for example : http://example.com/my-app
No messages received , nor sent
UPDATE: No sending was fixed by adding servletContext.getContextPath() to destination prefixes.
With context, I only got this:
["SUBSCRIBE\nid:sub-0\ndestination:/my-app/chat-messages/TST\n\n\u0000"]
["SEND\ndestination:/my-app/chat/message/TST\ncontent-length:52\n\n{\"message\":\"\",\"username\":\"USER\",\"event\":\"ONLINE\"}\u0000"]
Here is my configurations:
#Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
private static final String TILES = "/WEB-INF/tiles/tiles.xml";
private static final String VIEWS = "/WEB-INF/views/**/views.xml";
private static final String RESOURCES_HANDLER = "/resources/";
private static final String RESOURCES_LOCATION = RESOURCES_HANDLER + "**";
#Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping requestMappingHandlerMapping = super
.requestMappingHandlerMapping();
requestMappingHandlerMapping.setUseSuffixPatternMatch(false);
requestMappingHandlerMapping.setUseTrailingSlashMatch(false);
return requestMappingHandlerMapping;
}
#Bean
public TilesViewResolver configureTilesViewResolver() {
return new TilesViewResolver();
}
#Bean
public TilesConfigurer configureTilesConfigurer() {
TilesConfigurer configurer = new TilesConfigurer();
configurer.setDefinitions(TILES, VIEWS);
return configurer;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(RESOURCES_HANDLER).addResourceLocations(
RESOURCES_LOCATION);
}
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
WebSocketMesssageBroker
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{
#Autowired
private ServletContext servletContext;
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/chat-messages");
config.setApplicationDestinationPrefixes(servletContext.getContextPath() + "/chat");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat-websocket").withSockJS();
}
}
And I have a controller to process everything
#MessageMapping("/message/{projectId}")
#SendTo("/chat-messages/{projectId}")
public ChatResponse sendMessage(#DestinationVariable String projectId, MessageSent message) throw InterruptedException {
//Send reponse back like user online/offline or message posted
return new ChatResponse(chatMessage);
}
In JSP file I have following JS called
var socket = new SockJS('<c:url value="/chat-websocket/"/>');
stompClient.subscribe('<c:url value="/chat-messages/${chatProject.projectId}"/>', function (data) { ....SOME RESPONSE PROCESSING... });
stompClient.send("<c:url value="/chat/message/${chatProject.projectId}"/>", {}, JSON.stringify({.....PAYLOAD TO SEND ---}));
and web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<error-page>
<exception-type>org.springframework.security.web.authentication.rememberme.CookieTheftException</exception-type>
<location>/signin</location>
</error-page>
<error-page>
<location>/generalError</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/404</location>
</error-page>
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<trim-directive-whitespaces>true</trim-directive-whitespaces>
</jsp-property-group>
</jsp-config>
</web-app>
I do suspect that this might be something with configuration of either tiles or whole dispatcher in web.xml or something like this :/
Would be very greatfull for hints

I was finnaly able to resolve this issue. It turns out whenever I was creating SockJS subscriber I should pass relative path as param, without any context
(I presume the base websocket opened url already have correct url)
So in order to properly receive subscription events all I had to do was change
stompClient.subscribe('<c:url value="/chat-messages/${chatProject.projectId}"/>', function (data) { ....SOME RESPONSE PROCESSING... });
to this:
stompClient.subscribe('/chat-messages/${chatProject.projectId}', function (data) { ....SOME RESPONSE PROCESSING... });
(without the <c:url> which was returning context path all the time)
So whenever I tried to subscribe to chat-messages using <c:url value="chat-messages/ID">in fact I was subscribing to:
my-app/chat-messages/ID and my controller and config was expecting plain relative chat-messages
That's why after adding contextPath to WebSocketController setApplicationDestinationPrefixes, app started sending correct messages
Those are couple of hours I'm not getting back :)

Related

#Provider resource not registering in rest application

I am trying to register an interceptor for my rest application. The purpouse of this interceptor is to get a token inside the request to validate if the request is valid or not.
I have created a custom tag to achieve this:
#Provider
#Secured
public class AuthenticationFilter implements ContainerRequestFilter{
private static final Logger LOGGER = Logger.getLogger(AuthenticationFilter.class);
UserDAO userDAO = (UserDAO) SpringApplicationContext.getBean("userDAO");
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// Get the HTTP Authorization header from the request
String authorizationHeader =
requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
// Check if the HTTP Authorization header is present and formatted correctly
if (authorizationHeader == null || !authorizationHeader.startsWith("BSC")) {
if (authorizationHeader== null){
LOGGER.error("No authorization header");
} else{
LOGGER.error("Authorization header: " + authorizationHeader);
}
throw new NotAuthorizedException("Authorization header must be provided");
}
// Extract the token from the HTTP Authorization header
String token = authorizationHeader.substring("BSC".length());
// Validate the token
boolean ok = validateToken(token);
if (!ok){
LOGGER.error("Not authorized, passed token: " + token);
throw new NotAuthorizedException("Not authorized");
}
}
private boolean validateToken(String token){
boolean ok = userDAO.validateToken(token);
if (ok){
userDAO.updateToken(token);
}else{
userDAO.deleteToken(token);
}
return ok;
}
}
#NameBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE, ElementType.METHOD})
public #interface Secured {
}
All of the methods with the #Secured tag must pass throught the interceptor.
I have registered the interceptor and the rest service in a class that extends Application:
public class RestApplication extends Application{
private Set<Object> singletons = new HashSet<Object>();
public RestApplication() {
singletons.add(new RestService());
singletons.add(new AuthenticationFilter());
}
#Override
public Set<Object> getSingletons() {
return singletons;
}
}
Then in my web.xml I have registered this class:
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Manufacturing</display-name>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>jersey-serlvet</servlet-name>
<servlet-class>
com.sun.jersey.spi.container.servlet.ServletContainer
</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.everis.manufacturing.application.RestApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey-serlvet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
But it not seems to work, I am calling a service that have the #Secured tag but it isnĀ“t calling the interceptor.
Thanks in advance!
try changing the class RestApplication to extend ResourceConfig instead of Application.

Spring MVC 4 Java Config with Static HTML Views

I am trying to build a single page HTML/Angular app backed by Spring MVC 4 without .jsp files.
When the user arrives to the root URL (http://myapp.com/ for example), I want to send the single page HTML document (index.html).
However, I am new to Spring MVC's Java config and cannot work out the proper combination of ServletMapping, ViewResolver, and ResourceHandler.
I know that there may be some additional configuration for the "default" or "index" page, but I may be mistaken.
So how do I make a Java configured Spring MVC 4 application send an html file?
File structure
/src
'-/main
|-/web-inf
'-/webapp
|-index.html (want to send this)
'-/app
|-/partials
'-app.js
WebAppConfig.java
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/").addResourceLocations("/index.html");
}
IndexController.java
#RequestMapping("/")
public void index() {
System.out.println("pls"); // executes
// Have tried returning "index" and "index.html"
}
WebInitializer.java
#Override
protected String[] getServletMappings() {
// have tried with "/" as well
return new String[] { "/*" };
}
I think you should add this:
<mvc:view-controller path="/" view-name="index"/>
in your dispathcer-servlet file.
Here are a few working solutions either way - in case anyone needs one for their specific use case.
If you DO NOT want to invoke a controller method and just serve the file.
WebInitializer.java:
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
Web.xml
<web-app>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
WebConfig.java
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("index.html").addResourceLocations("/index.html ");
}
If you DO want to invoke a controller method on landing.
No welcome file list is required in the web.xml
WebInitializer.java:
#Override
protected String[] getServletMappings() {
return new String[] { "/*" };
}
WebConfig.java
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("index.html").addResourceLocations("/index.html ");
}
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setSuffix(".html");
return viewResolver;
}
Controller.java
#RequestMapping("/")
public String index() {
return "index";
}
Hope that helps somebody.

Mixing web.xml and AbstractAnnotationConfigDispatcherServletInitializer in Spring

I have an application on Spring and using Java Configs to configure and initialize my application, so that I have no web.xml. Here is how my web initializer looks like,
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{PublicApiConfig.class, MobileConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/*"};
}
#Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
LoggingFilter loggingFilter = new LoggingFilter();
return new Filter[]{characterEncodingFilter, loggingFilter};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[0];
}
}
I need to implement tomcat session replication, and for the sake of purpose I need to have application as distributable. With traditional web.xml I could add <distributable/> attribute and thats it. However as far as I understand there is no way to do this via Java Configs.
My question is if it is possible to have mixed web.xml and java configs, e.g. to have
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<distributable/>
</web-app>
and include it in WebInitializer.
According to the Servlet 3.0 specification its possible to mix web.xml with Programmatic servlet registration as long as web-app version >= 3.0 and metadata-complete attribute is false (default). With your current configuration it should work
You can use a TomcatEmbeddedServletContainerFactory, and there
#Override
public void customize(Context context){
context.setDistributable(true);
}
You find a complete code example in this thread
spring-boot-application-with-embedded-tomcat-session-clustering
Edit : I am not using Spring Boot in this case, and TomcatEmbeddedServletContainerFactory is not available
The javadoc of WebApplicationInitializer says, its possible to use it together with a web.xml :
WEB-INF/web.xml and WebApplicationInitializer use are not mutually exclusive; for example, web.xml can register one servlet, and a WebApplicationInitializer can register another. An initializer can even modify registrations performed in web.xml through methods such as ServletContext#getServletRegistration(String).

HTTP error 404 from a RESTful web service deployed in Jboss 7.1.1

I have successfully deployed a small RESTful web service in Jboss 7.1.1, but when I tried testing it using this url: http://localhost:8080/auth/webresources/test/hello/Jumbu it returns HTTP error 404. the code for the service is below:
#Path("/test")
public class TestService {
#GET
#Path("hello/{name}")
public String sayHello(#PathParam("name") String name) {
return "Welcome " + name;
}
}
my ApplicationConfig class is given below:
#ApplicationPath("/webresources")
public class ApplicationConfig extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new java.util.HashSet<Class<?>>();
addRestResourceClasses(resources);
return resources;
}
private void addRestResourceClasses(Set<Class<?>> resources) {
resources.add(com.jcjoof.jcjoofaws.TestService.class);
}
}
I can't really figure out what is wrong, Kindly help..
I believe your sayHello method is missing a slash.. Try using #Path("/hello/{name}")
Also, try adding an empty web.xml
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
</web-app>

Spring Rest Json Service inside a Felix OSGi container

So im trying to create a remote Rest (JSON) service inside an OSGi bundle based in Felix with Maven.
my basic service interface :
#Controller
#RequestMapping("/s/fileService")
public interface RestFileService {
#RequestMapping(value = "/file", method = RequestMethod.POST)
#ResponseBody
public String getFile(Long id);
}
My implementation of the interface
public class RestFileServiceImpl implements RestFileService{
public String getFile(Long id) {
return "test service";
}
}
Normally i would add this to my web.xml
<servlet>
<servlet-name>spring-mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/application-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-mvc-dispatcher</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
And this would work fine inside a normal webapp.
But now i want to put this inside an OSGi bundle.
Servlet 3.0 allows you to use #WebServlet to declare a servlet without the web.xml
So i created a RestServlet
#WebServlet(value="/rest", name="rest-servlet")
public class RestServlet implements ServletContextListener {
private static Log sLog = LogFactory.getLog(RestServlet.class);
public void contextInitialized(ServletContextEvent arg0) {
sLog.info("initializing the Rest Servlet");
}
public void contextDestroyed(ServletContextEvent arg0) {
sLog.info("un-initializing the Rest Servlet");
}
}
This is my OSGi activator:
public class Activator implements BundleActivator {
private static Log sLog = LogFactory.getLog(Activator.class);
public void start(BundleContext context) throws Exception {
/*
* Exposing the Servlet
*/
Dictionary properties = new Hashtable();
context.registerService(RestFileService.class.getName(), new RestFileServiceImpl(), properties );
sLog.info("Registered Remote Rest Service");
}
public void stop(BundleContext context) throws Exception {
sLog.info("Unregistered Remote Rest Service");
}
}
I know Felix has its own http implementation with JAX but im trying to do this with spring annotations and as little XML as possible.
Can i force it to register the annotation driven 3.0 servlet ?
What am i doing wrong ? is this even possible ?
If you're looking for an easy way to do REST in OSGi, take a look at some of the web components provided by the Amdatu project. This page pretty much explains how to create a REST service: https://amdatu.org/application/web/ and there is also a video which will talk you through the whole process: https://amdatu.org/generaltop/videolessons/

Categories

Resources