I'm trying to build a java websocket server. I've written a simple server endpoint:
#ServerEndpoint(value = "/test")
public class EndPoint {
static Queue<Session> queue = new ConcurrentLinkedQueue<Session>();
public static void send(int a, int b) {
try {
for(Session session : queue) {
session.getBasicRemote().sendText("a = " + a + ",b=" + b);
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
#OnOpen
public void openConnection(Session session) {
queue.add(session);
}
#OnClose
public void closeConnection(Session session) {
queue.remove(session);
}
#OnError
public void error(Session session, Throwable t) {
queue.remove(session);
}
}
My web.xml looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 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_2_5.xsd">
</web-app>
When I hit ws://localhost:8080/websocket-1.0/test from Javascript, I get a 404 response. I'm using tomcat 8 to deploy this. What am I missing here?
1) Use latest version of Tomcat 7.0.x or latest version of Tomcat 8.0.x
2) Make sure your war contains only the ServerEndpoint classes and an empty web.xml. Tomcat automatically scans and loads the classes annotated with ServerEndpoint annotation
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"
metadata-complete="true">
</web-app>
3) Verify that catalina.out has the following log msg and there are no exceptions.
org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive /usr/local/tomcat/apache-tomcat-8.0.12/webapps/.war
According to this link https://tyrus.java.net/documentation/1.4/index/websocket-api.html and my expereince the endpoint should match the ws: url
E.g.
#ServerEndpoint(value = "/test")
ws://localhost:8080/test
or
#ServerEndpoint(value = "/websocket-1.0//test")
ws://localhost:8080/websocket-1.0/test
Related
I have a rest jersey application like this
#ApplicationPath("/rest")
public class HelloApp extends Application
{
public HelloApp() throws Exception {
//also tried throwing Runtimeexcpetion
throw new Exception();
}
#Override
public Set<Class<?>> getClasses()
{
Set<Class<?>> s = new HashSet<Class<?>>();
s.add(HelloWorldService.class);
return s;
}
}
This is the web.xml file
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" 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">
<display-name>REST Web Application Demo</display-name>
<servlet>
<servlet-name>jersey.hello</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>rest.demo.HelloApp</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey.hello</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>
I packaged the above to a war file and then used in the following tomcat embedded program
public class WarExample {
public static void main(String[] args) throws ServletException, LifecycleException {
Tomcat tomcat = new Tomcat();
tomcat.setBaseDir("temp");
tomcat.setPort(8080);
String contextPath = "/";
String warFilePath = "path to war file";
tomcat.getHost().setAppBase(".");
Context context = tomcat.addWebapp(contextPath, warFilePath);
context.addLifecycleListener(new LifecycleListener() {
public void lifecycleEvent(LifecycleEvent event) {
System.out.println(event.getLifecycle().getStateName());
//do something if event.getLifecycle().getState() == LifecycleState.FAILED
}
});
tomcat.start();
tomcat.getServer().await();
}
}
I added a lifecycle listener to context that is returned by adding war as webapp. I am getting Starting and Started states but not seeing any other states here.
Even though i am throwing an exception/runtimeexception from my jersey rest application why is tomcat embedded still not throwing failed state.
i had to add this for tomcat to publish failed state
final Host host = tomcat.getHost();
if (host instanceof StandardHost) {
((StandardHost) tomcat.getHost()).setFailCtxIfServletStartFails(true);
}
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 :)
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).
I can't fathom why this isn't working. Inside a Jersey resource method I make an asynchronous call, but the code blocks and never returns. Testing with curl, my terminal just hangs. From application logs, I can tell the request got intercepted and the code was executed.
Using Jersey 2.8 with maven tomcat7 plugin.
I also tried using an Executor Service instead of creating the thread directly, this is how I'd prefer to do it. Changed to creating a thread directly to be close to the documentation.
Jersey code:
#Path("/trade")
public class TradeEndpoint {
// TODO: make this configurable!!
private ExecutorService threadPool = Executors.newFixedThreadPool(10);
private static MyService myService;
static {
//issues getting Spring working, so get it like this
orderFillService = Bootstrapper.getOrderFillService();
}
#GET
public String getFoo() {
return "Foo";
}
#POST
#Path("limit")
#Consumes({ MediaType.APPLICATION_JSON })
public void limitOrder(#Suspended final AsyncResponse response, Model model) {
System.out.println(model);
new Thread( new Runnable() {
public void run() {
myService.doStuff(model);
}
}).start();
System.out.println("SUBMITTED");
}
Web.xml
<web-app version="2.5" 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_2_5.xsd">
<servlet>
<servlet-name>web</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.foo.conf.Application</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>web</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>
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>