How can I let spring+cometd support webscoket? - spring

I use chrome, it always return:
WebSocket connection to 'ws://192.168.1.228:9090/achat/cometd' failed: Unexpected response code: 400
After this error, it use long-polling transport, and success to connect to the server.
How can I let WebSocket work? Is it caused by using SpringFramework 3.1? Thanks.
Web.xml
<!-- Filter to support cross domain requests -->
<filter>
<filter-name>cross-origin</filter-name>
<filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class>
<!--async-supported>true</async-supported-->
</filter>
<filter-mapping>
<filter-name>cross-origin</filter-name>
<url-pattern>/cometd/*</url-pattern>
</filter-mapping>
<!-- Cometd Servlet -->
<servlet>
<servlet-name>cometd</servlet-name>
<servlet-class>org.cometd.annotation.AnnotationCometdServlet</servlet-class>
<!--async-supported>true</async-supported-->
<init-param>
<param-name>timeout</param-name>
<param-value>20000</param-value>
</init-param>
<init-param>
<param-name>interval</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>maxInterval</param-name>
<param-value>10000</param-value>
</init-param>
<init-param>
<param-name>maxLazyTimeout</param-name>
<param-value>5000</param-value>
</init-param>
<init-param>
<param-name>long-polling.multiSessionInterval</param-name>
<param-value>2000</param-value>
</init-param>
<init-param>
<param-name>logLevel</param-name>
<param-value>100</param-value>
</init-param>
<init-param>
<param-name>transports</param-name>
<param-value>org.cometd.websocket.server.WebSocketTransport</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>cometd</servlet-name>
<url-pattern>/cometd/*</url-pattern>
</servlet-mapping>
Servlet
#Named
#Singleton
#Service
public class ChatService {
#Inject
private BayeuxServer bayeux;
#Session
private ServerSession serverSession;
#Listener("/chat")
public void processHello(ServerSession remote, ServerMessage.Mutable message)
{
Map<String, Object> input = message.getDataAsMap();
String room = input.get("room").toString();
String content = input.get("content").toString();
Map<String, Object> output = input;
bayeux.getChannel("/room/" + room).publish(remote, output, null);
}
}

Follow the CometD Reference Manual, specifically the section that explains how to integrate CometD with Spring.
There you can find an example that uses Spring's XML configuration to setup WebSocket, and an example of how to configure Spring via annotations.
Your question lacks the Spring configuration file and part of the web.xml file so it is difficult to help you in a more detailed way.
Just remember that if you configure CometD in Spring, then you must remove all the CometD configuration from web.xml. You must have only one place where you configure things: either web.xml or Spring's XML/annotations.

Related

Using DispatcherServlet for RestController

I'm currently trying to understand how the Dispatcher Servlet works with the Rest Controller ,but Postman returns 404 on everything I tried thus far.
The rest controller
#RestController
#RequestMapping(value = "/applications")
public class ApplicationController {
private static final Logger logger = LoggerFactory.getLogger(ApplicationController.class);
#Autowired
#Qualifier("ApplDAO")
private ApplDAO applDAO;
#Autowired
ApplicationService objServices;
#RequestMapping(value = "for_user\\{username:\\d+}", method = RequestMethod.GET)
public Application getApp(#PathVariable("username") String username){
Application app = applDAO.getByUsername(username);
return app;
}
}
My web.xml
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring4-servlet.xml</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>springDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
I tried using url-pattern /* but with no results.
This is the url I was trying to access http://localhost:8080/project/applications/for_user/username:acid
Is there something wrong with the URL I'm using or have I used the dispatcher wrong.
Here is the spring error
No mapping found for HTTP request with URI [/project/applications/for_user/username:acid
Answered by JB Nizet
Why do you use backslashes instead of slashes in your RequestMapping?
Why do you use the regex \d+ if you want to send username:acid (or
acid?) as user name. Just use value = "/for_user/{username}", and use
http://localhost:8080/project/applications/for_user/acid.

cxf and spring MVC : No service was found

I have spring application, in which I use org.apache.cxf for soap and spring MVC for displayng some pages.
My web.xml contains two servlets :CXFServlet and mvc-dispatcher
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/servlet-context.xml</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
When I has been used #ResponseBody in my controller everything was fine.
#Controller
#RequestMapping("/hello")
#ResponseBody
public class HelloController {
#RequestMapping(method = RequestMethod.GET)
public String printWelcome() {
return "hello" ;
}
}
but then i was needed to use jsp I have to use the following
#Controller
#RequestMapping("/hello")
public class HelloController {
#RequestMapping(method = RequestMethod.GET)
public ModelAndView printWelcome(ModelMap model) {
model.addAttribute("message", "hello");
return new ModelAndView("hello") ;
}
}
and when I request http://localhost:8080/hello I get "No service was found" instead of "hello"
I found that if I delete following from web.xml
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
my controller works fine.
The Servlet container you are using is matching CXFServlet instead of mvc-dispatcher for the URI http://localhost:8080/hello, resulting in your request being sent to CXFServlet, and the error message "No service was found" being returned by CXFServlet. To quote the Servlet 3.0 spec,
Versions of this specification prior to 2.5 made use of these mapping
techniques as a suggestion rather than a requirement, allowing servlet
containers to each have their different schemes for mapping client
requests to servlets.
http://download.oracle.com/otndocs/jcp/servlet-3.0-fr-eval-oth-JSpec/
You will likely need to configure you CXFServlet mapping to something else, e.g.
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
You might want to mention the container (Tomcat, Glassfish, etc.) that you are using, as there could also be a bug preventing this from working correctly.

what is the "default applicationContext" in Jersey?

I am using Jersey to perform acceptance tests on RESTful web services. It appears as though my applicationContext.xml is not being loaded when the client loads. I see the following log output:
INFO: Using default applicationContext
Is this "default" file soemthing that Jersey loads when it cannot find my file? Or does this indicate that my file was found?
#ContextConfiguration(locations={"/applicationContext.xml", "/applicationContextTest.xml"})
public class BaseResourceTest extends JerseyTest {
final static URI baseUri = UriBuilder.fromUri( "http://localhost" ).port( 9998 ).build();
public BaseResourceTest() throws Exception {
super(new WebAppDescriptor.Builder("xxx.yyy.zzz").contextPath(baseUri.getPath())
.contextParam(
SpringServlet.CONTEXT_CONFIG_LOCATION, "classpath:applicationContextTest.xml" )
.servletClass(SpringServlet.class )
.contextListenerClass( ContextLoaderListener.class )
.build());
}
.......
some tests
.......
}
my web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>xxx.yyy.LoggingAssuranceListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>jersey-servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>xxx.yyy.zzzz</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.property.WadlGeneratorConfig</param-name>
<param-value>xxx.yyy.zzz.BroadsoftWadlGeneratorConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey-servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>

Dispatcher servlet mapping

I am trying to use spring-security
Before all of the configuration
http://localhost:9090/app/login2.xhtml
request, works as i expected.
I added a controller:
#Controller
#RequestMapping("/auth")
public class LoginController {
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String getLoginPage(#RequestParam(value="error", required=false) boolean error,
ModelMap model) {
return "login2.xhtnml";
}
}
I have in web.xml:
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:META-INF/spring-servlet.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
With this configuration when i call
http://localhost:9090/app/login2.xhtml
Error comes
WARN org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/app/login2.xhtml] in DispatcherServlet with name 'spring'
BUT when i change configuration mapping to
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
http://localhost:9090/app/login2.xhtml works as i expected
but
http://localhost:9090/app/auth/login
gives no error, no exception, no redirection, i think dispatcher servlet can not know about this request.
http://localhost:9090/app/app/auth/login
works with
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
My understanding:
dispatcher servlet use "http://localhost:9090/" as base for searching login2.xhtml
and use "http://localhost:9090/app" for /auth/login URL.
I do not know where to set this, and why they are different.
Have you added the SpringSecurityFilterChain to the web.xml?
<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>
Could you past the registered "Request Bindings" if the container starts (from the logfile)?

Configuring Grizzly with a web.xml

I can start grizzly and deploy Jersey webservices on it with the following lines.
protected HttpServer create() throws Throwable {
ResourceConfig rc = new PackagesResourceConfig("com.resource", "com.provider");
HttpServer server = GrizzlyServerFactory.createHttpServer(uri, rc);
return server;
}
But is there a way to load a web.xml instead of a ResourceConfig?
<web-app>
<servlet>
<servlet-name>Jersey</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.resource, com.provider</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Jersey</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
It seems that there is currently no direct way to configure grizzly with a web.xml. However I have used a partial solution that may be a beginning.
web.xml
First to understand the solution, we must understand what is the meaning of using a web.xml. It is basically use for configure your web application (see this answer for a more detail). In this case we are configuring init-params for the servlet.
The (partial) solution
Instead of using the web.xml and instead of using ResouceConfig.class, we can use Grizzly as our servlet and initializing the parameters.
For example
<web-app>
<servlet>
<servlet-name>Jersey</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.resource, com.provider</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Jersey</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
would give something like :
protected HttpServer create() throws Throwable {
HashMap<String, String> initParams = new HashMap<>();
//ServerProperties.PROVIDER_PACKAGES is equal to "jersey.config.server.provider.packages"
initParams.put(ServerProperties.PROVIDER_PACKAGES, "com.resource,com.provider");
//Make sure to end the URI with a forward slash
HttpServer server = GrizzlyWebContainerFactory.create("http://localhost:8080/", initParams);
return server;
}
With this, we can therefore put all the init-params that we want to.
However this solution cannot replace a whole web.xml.

Resources