{"id":19358,"date":"2013-12-03T19:00:44","date_gmt":"2013-12-03T17:00:44","guid":{"rendered":"http:\/\/www.javacodegeeks.com\/?p=19358"},"modified":"2013-12-02T22:58:20","modified_gmt":"2013-12-02T20:58:20","slug":"java-websockets-jsr-356-on-jetty-9-1","status":"publish","type":"post","link":"https:\/\/www.javacodegeeks.com\/2013\/12\/java-websockets-jsr-356-on-jetty-9-1.html","title":{"rendered":"Java WebSockets (JSR-356) on Jetty 9.1"},"content":{"rendered":"<p><a href=\"http:\/\/www.eclipse.org\/jetty\/documentation\/current\/jetty-javaee.html\">Jetty 9.1<\/a> is finally released, bringing <a href=\"http:\/\/jcp.org\/en\/jsr\/detail?id=356\">Java WebSockets (JSR-356)<\/a> to non-EE environments. It&#8217;s awesome news and today&#8217;s post will be about using this great new API along with <a href=\"http:\/\/projects.spring.io\/spring-framework\/\">Spring Framework<\/a>.<\/p>\n<p><a href=\"http:\/\/jcp.org\/en\/jsr\/detail?id=356\">JSR-356<\/a> defines concise, annotation-based model to allow modern Java web applications easily create bidirectional communication channels using <a href=\"http:\/\/www.w3.org\/TR\/websockets\/\">WebSockets API<\/a>. It covers not only server-side, but client-side as well, making this API really simple to use everywhere.<\/p>\n<p>Let&#8217;s get started! Our goal would be to build a <a href=\"http:\/\/www.w3.org\/TR\/websockets\/\">WebSockets<\/a> server which accepts messages from the clients and broadcasts them to all other clients currently connected. To begin with, let&#8217;s define the message format, which server and client will be exchanging, as this simple <b>Message<\/b> class. We can limit ourselves to something like a <b>String<\/b>, but I would like to introduce to you the power of another new API &#8211; <a href=\"http:\/\/jcp.org\/en\/jsr\/detail?id=353\">Java API for JSON Processing (JSR-353)<\/a>.<\/p>\n<pre class=\" brush:java\">package com.example.services;\r\n\r\npublic class Message {\r\n    private String username;\r\n    private String message;\r\n\r\n    public Message() {\r\n    }\r\n\r\n    public Message( final String username, final String message ) {\r\n        this.username = username;\r\n        this.message = message;\r\n    }\r\n\r\n    public String getMessage() {\r\n        return message;\r\n    }\r\n\r\n    public String getUsername() {\r\n        return username;\r\n    }\r\n\r\n    public void setMessage( final String message ) {\r\n        this.message = message;\r\n    }\r\n\r\n    public void setUsername( final String username ) {\r\n        this.username = username;\r\n    }\r\n}<\/pre>\n<p>To separate the declarations related to the server and the client, <a href=\"http:\/\/jcp.org\/en\/jsr\/detail?id=356\">JSR-356<\/a> defines two basic annotations: <b>@ServerEndpoint<\/b> and <b>@ClientEndpoit<\/b> respectively. Our client endpoint, let&#8217;s call it <b>BroadcastClientEndpoint<\/b>, will simply listen for messages server sends:<\/p>\n<pre class=\" brush:java\">package com.example.services;\r\n\r\nimport java.io.IOException;\r\nimport java.util.logging.Logger;\r\n\r\nimport javax.websocket.ClientEndpoint;\r\nimport javax.websocket.EncodeException;\r\nimport javax.websocket.OnMessage;\r\nimport javax.websocket.OnOpen;\r\nimport javax.websocket.Session;\r\n\r\n@ClientEndpoint\r\npublic class BroadcastClientEndpoint {\r\n    private static final Logger log = Logger.getLogger( \r\n        BroadcastClientEndpoint.class.getName() );\r\n\r\n    @OnOpen\r\n    public void onOpen( final Session session ) throws IOException, EncodeException  {\r\n        session.getBasicRemote().sendObject( new Message( \"Client\", \"Hello!\" ) );\r\n    }\r\n\r\n    @OnMessage\r\n    public void onMessage( final Message message ) {\r\n        log.info( String.format( \"Received message '%s' from '%s'\",\r\n            message.getMessage(), message.getUsername() ) );\r\n    }\r\n}<\/pre>\n<p>That&#8217;s literally it! Very clean, self-explanatory piece of code: <b>@OnOpen<\/b> is being called when client got connected to the server and @<b>OnMessage<\/b> is being called every time server sends a message to the client. Yes, it&#8217;s very simple but there is a caveat: <a href=\"http:\/\/jcp.org\/en\/jsr\/detail?id=356\">JSR-356<\/a> implementation can handle any simple objects but not the complex ones like <b>Message<\/b> is. To manage that, <a href=\"http:\/\/jcp.org\/en\/jsr\/detail?id=356\">JSR-356<\/a> introduces concept of <b>encoders<\/b> and <b>decoders<\/b>.<\/p>\n<p>We all love <a href=\"http:\/\/www.json.org\/\">JSON<\/a>, so why don&#8217;t we define our own <a href=\"http:\/\/www.json.org\/\">JSON<\/a> encoder and decoder? It&#8217;s an easy task which <a href=\"http:\/\/jcp.org\/en\/jsr\/detail?id=353\">Java API for JSON Processing (JSR-353)<\/a> can handle for us. To create an encoder, you only need to implement <b>Encoder.Text&lt; Message &gt;<\/b> and basically serialize your object to some string, in our case to <a href=\"http:\/\/www.json.org\/\">JSON<\/a> string, using <a href=\"http:\/\/docs.oracle.com\/javaee\/7\/api\/javax\/json\/JsonObjectBuilder.html\">JsonObjectBuilder<\/a>.<\/p>\n<pre class=\" brush:java\">package com.example.services;\r\n\r\nimport javax.json.Json;\r\nimport javax.json.JsonReaderFactory;\r\nimport javax.websocket.EncodeException;\r\nimport javax.websocket.Encoder;\r\nimport javax.websocket.EndpointConfig;\r\n\r\npublic class Message {\r\n    public static class MessageEncoder implements Encoder.Text&lt; Message &gt; {\r\n        @Override\r\n        public void init( final EndpointConfig config ) {\r\n        }\r\n\r\n        @Override\r\n        public String encode( final Message message ) throws EncodeException {\r\n            return Json.createObjectBuilder()\r\n                .add( \"username\", message.getUsername() )\r\n                .add( \"message\", message.getMessage() )\r\n                .build()\r\n                .toString();\r\n        }\r\n\r\n        @Override\r\n        public void destroy() {\r\n        }\r\n    }\r\n}<\/pre>\n<p>For decoder part, everything looks very similar, we have to implement <b>Decoder.Text&lt; Message &gt;<\/b> and deserialize our object from string, this time using <a href=\"http:\/\/docs.oracle.com\/javaee\/7\/api\/javax\/json\/JsonReader.html\">JsonReader<\/a>.<\/p>\n<pre class=\" brush:java\">package com.example.services;\r\n\r\nimport javax.json.JsonObject;\r\nimport javax.json.JsonReader;\r\nimport javax.json.JsonReaderFactory;\r\nimport javax.websocket.DecodeException;\r\nimport javax.websocket.Decoder;\r\n\r\npublic class Message {\r\n    public static class MessageDecoder implements Decoder.Text&lt; Message &gt; {\r\n        private JsonReaderFactory factory = Json.createReaderFactory( Collections.&lt; String, Object &gt;emptyMap() );\r\n\r\n        @Override\r\n        public void init( final EndpointConfig config ) {\r\n        }\r\n\r\n        @Override\r\n        public Message decode( final String str ) throws DecodeException {\r\n            final Message message = new Message();\r\n\r\n            try( final JsonReader reader = factory.createReader( new StringReader( str ) ) ) {\r\n                final JsonObject json = reader.readObject();\r\n                message.setUsername( json.getString( \"username\" ) );\r\n                message.setMessage( json.getString( \"message\" ) );\r\n            }\r\n\r\n            return message;\r\n        }\r\n\r\n        @Override\r\n        public boolean willDecode( final String str ) {\r\n            return true;\r\n        }\r\n\r\n        @Override\r\n        public void destroy() {\r\n        }\r\n    }\r\n}<\/pre>\n<p>And as a final step, we need to tell the client (and the server, they share same decoders and encoders) that we have encoder and decoder for our messages. The easiest thing to do that is just by declaring them as part of <b>@ServerEndpoint<\/b> and <b>@ClientEndpoit<\/b> annotations.<div style=\"display:inline-block; margin: 15px 0;\"> <div id=\"adngin-JavaCodeGeeks_incontent_video-0\" style=\"display:inline-block;\"><\/div> <\/div><\/p>\n<pre class=\" brush:java\">import com.example.services.Message.MessageDecoder;\r\nimport com.example.services.Message.MessageEncoder;\r\n\r\n@ClientEndpoint( encoders = { MessageEncoder.class }, decoders = { MessageDecoder.class } )\r\npublic class BroadcastClientEndpoint {\r\n}<\/pre>\n<p>To make client&#8217;s example complete, we need some way to connect to the server using <b>BroadcastClientEndpoint<\/b> and basically exchange messages. The <b>ClientStarter<\/b> class finalizes the picture:<\/p>\n<pre class=\" brush:java\">package com.example.ws;\r\n\r\nimport java.net.URI;\r\nimport java.util.UUID;\r\n\r\nimport javax.websocket.ContainerProvider;\r\nimport javax.websocket.Session;\r\nimport javax.websocket.WebSocketContainer;\r\n\r\nimport org.eclipse.jetty.websocket.jsr356.ClientContainer;\r\n\r\nimport com.example.services.BroadcastClientEndpoint;\r\nimport com.example.services.Message;\r\n\r\npublic class ClientStarter {\r\n    public static void main( final String[] args ) throws Exception {\r\n        final String client = UUID.randomUUID().toString().substring( 0, 8 );\r\n\r\n        final WebSocketContainer container = ContainerProvider.getWebSocketContainer();    \r\n        final String uri = \"ws:\/\/localhost:8080\/broadcast\";  \r\n\r\n        try( Session session = container.connectToServer( BroadcastClientEndpoint.class, URI.create( uri ) ) ) {\r\n            for( int i = 1; i &lt;= 10; ++i ) {\r\n                session.getBasicRemote().sendObject( new Message( client, \"Message #\" + i ) );\r\n                Thread.sleep( 1000 );\r\n            }\r\n        }\r\n\r\n        \/\/ Application doesn't exit if container's threads are still running\r\n        ( ( ClientContainer )container ).stop();\r\n    }\r\n}<\/pre>\n<p>Just couple of comments what this code does: we are connecting to <a href=\"http:\/\/www.w3.org\/TR\/websockets\/\">WebSockets<\/a> endpoint at <b>ws:\/\/localhost:8080\/broadcast<\/b>, randomly picking some <b>client<\/b> name (from UUID) and generating 10 messages, every with 1 second delay (just to be sure we have time to receive them all back).<\/p>\n<p>Server part doesn&#8217;t look very different and at this point could be understood without any additional comments (except may be the fact that server just broadcasts every message it receives to all connected clients). Important to mention here: new instance of the server endpoint is created every time new client connects to it (that&#8217;s why <b>peers<\/b> collection is static), it&#8217;s a default behavior and could be easily changed.<\/p>\n<pre class=\" brush:java\">package com.example.services;\r\n\r\nimport java.io.IOException;\r\nimport java.util.Collections;\r\nimport java.util.HashSet;\r\nimport java.util.Set;\r\n\r\nimport javax.websocket.EncodeException;\r\nimport javax.websocket.OnClose;\r\nimport javax.websocket.OnMessage;\r\nimport javax.websocket.OnOpen;\r\nimport javax.websocket.Session;\r\nimport javax.websocket.server.ServerEndpoint;\r\n\r\nimport com.example.services.Message.MessageDecoder;\r\nimport com.example.services.Message.MessageEncoder;\r\n\r\n@ServerEndpoint( \r\n    value = \"\/broadcast\", \r\n    encoders = { MessageEncoder.class }, \r\n    decoders = { MessageDecoder.class } \r\n) \r\npublic class BroadcastServerEndpoint {\r\n    private static final Set&lt; Session &gt; sessions = \r\n        Collections.synchronizedSet( new HashSet&lt; Session &gt;() ); \r\n\r\n    @OnOpen\r\n    public void onOpen( final Session session ) {\r\n        sessions.add( session );\r\n    }\r\n\r\n    @OnClose\r\n    public void onClose( final Session session ) {\r\n        sessions.remove( session );\r\n    }\r\n\r\n    @OnMessage\r\n    public void onMessage( final Message message, final Session client ) \r\n            throws IOException, EncodeException {\r\n        for( final Session session: sessions ) {\r\n            session.getBasicRemote().sendObject( message );\r\n        }\r\n    }\r\n}<\/pre>\n<p>In order this endpoint to be available for connection, we should start the <a href=\"http:\/\/www.w3.org\/TR\/websockets\/\">WebSockets<\/a> container and register this endpoint inside it. As always, <a href=\"http:\/\/www.eclipse.org\/jetty\/documentation\/current\/jetty-javaee.html\">Jetty 9.1<\/a> is runnable in embedded mode effortlessly:<\/p>\n<pre class=\" brush:java\">package com.example.ws;\r\n\r\nimport org.eclipse.jetty.server.Server;\r\nimport org.eclipse.jetty.servlet.DefaultServlet;\r\nimport org.eclipse.jetty.servlet.ServletContextHandler;\r\nimport org.eclipse.jetty.servlet.ServletHolder;\r\nimport org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;\r\nimport org.springframework.web.context.ContextLoaderListener;\r\nimport org.springframework.web.context.support.AnnotationConfigWebApplicationContext;\r\n\r\nimport com.example.config.AppConfig;\r\n\r\npublic class ServerStarter  {\r\n    public static void main( String[] args ) throws Exception {\r\n        Server server = new Server( 8080 );\r\n\r\n        \/\/ Create the 'root' Spring application context\r\n        final ServletHolder servletHolder = new ServletHolder( new DefaultServlet() );\r\n        final ServletContextHandler context = new ServletContextHandler();\r\n\r\n        context.setContextPath( \"\/\" );\r\n        context.addServlet( servletHolder, \"\/*\" );\r\n        context.addEventListener( new ContextLoaderListener() );   \r\n        context.setInitParameter( \"contextClass\", AnnotationConfigWebApplicationContext.class.getName() );\r\n        context.setInitParameter( \"contextConfigLocation\", AppConfig.class.getName() );\r\n\r\n        server.setHandler( context );\r\n        WebSocketServerContainerInitializer.configureContext( context );        \r\n\r\n        server.start();\r\n        server.join(); \r\n    }\r\n}<\/pre>\n<p>The most important part of the snippet above is <b>WebSocketServerContainerInitializer.configureContext<\/b>: it&#8217;s actually creates the instance of <a href=\"http:\/\/www.w3.org\/TR\/websockets\/\">WebSockets<\/a> container. Because we haven&#8217;t added any endpoints yet, the container basically sits here and does nothing. <a href=\"http:\/\/projects.spring.io\/spring-framework\/\">Spring Framework<\/a> and <b>AppConfig<\/b> configuration class will do this last wiring for us.<\/p>\n<pre class=\" brush:java\">package com.example.config;\r\n\r\nimport javax.annotation.PostConstruct;\r\nimport javax.inject.Inject;\r\nimport javax.websocket.DeploymentException;\r\nimport javax.websocket.server.ServerContainer;\r\nimport javax.websocket.server.ServerEndpoint;\r\nimport javax.websocket.server.ServerEndpointConfig;\r\n\r\nimport org.eclipse.jetty.websocket.jsr356.server.AnnotatedServerEndpointConfig;\r\nimport org.springframework.context.annotation.Bean;\r\nimport org.springframework.context.annotation.Configuration;\r\nimport org.springframework.web.context.WebApplicationContext;\r\n\r\nimport com.example.services.BroadcastServerEndpoint;\r\n\r\n@Configuration\r\npublic class AppConfig  {\r\n    @Inject private WebApplicationContext context;\r\n    private ServerContainer container;\r\n\r\n    public class SpringServerEndpointConfigurator extends ServerEndpointConfig.Configurator {\r\n        @Override\r\n        public &lt; T &gt; T getEndpointInstance( Class&lt; T &gt; endpointClass ) \r\n                throws InstantiationException {\r\n            return context.getAutowireCapableBeanFactory().createBean( endpointClass );   \r\n        }\r\n    }\r\n\r\n    @Bean\r\n    public ServerEndpointConfig.Configurator configurator() {\r\n        return new SpringServerEndpointConfigurator();\r\n    }\r\n\r\n    @PostConstruct\r\n    public void init() throws DeploymentException {\r\n        container = ( ServerContainer )context.getServletContext().\r\n            getAttribute( javax.websocket.server.ServerContainer.class.getName() );\r\n\r\n        container.addEndpoint( \r\n            new AnnotatedServerEndpointConfig( \r\n                BroadcastServerEndpoint.class, \r\n                BroadcastServerEndpoint.class.getAnnotation( ServerEndpoint.class )  \r\n            ) {\r\n                @Override\r\n                public Configurator getConfigurator() {\r\n                    return configurator();\r\n                }\r\n            }\r\n        );\r\n    }  \r\n}<\/pre>\n<p>As we mentioned earlier, by default container will create new instance of server endpoint every time new client connects, and it does so by calling constructor, in our case <b>BroadcastServerEndpoint.class.newInstance()<\/b>. It might be a desired behavior but because we are using <a href=\"http:\/\/projects.spring.io\/spring-framework\/\">Spring Framework<\/a> and dependency injection, such new objects are basically unmanaged beans. Thanks to very well-thought (in my opinion) design of <a href=\"http:\/\/jcp.org\/en\/jsr\/detail?id=356\">JSR-356<\/a>, it&#8217;s actually quite easy to provide your own way of creating endpoint instances by implementing <b>ServerEndpointConfig.Configurator<\/b>. The <b>SpringServerEndpointConfigurator<\/b> is an example of such implementation: it&#8217;s creates new managed bean every time new endpoint instance is being asked (if you want single instance, you can create singleton of the endpoint as a bean in <b>AppConfig<\/b> and return it all the time).<\/p>\n<p>The way we retrieve the <a href=\"http:\/\/www.w3.org\/TR\/websockets\/\">WebSockets<\/a> container is <a href=\"http:\/\/www.eclipse.org\/jetty\">Jetty<\/a>-specific: from the attribute of the context with name <b>&#8220;javax.websocket.server.ServerContainer&#8221;<\/b> (it probably might change in the future). Once container is there, we are just adding new (managed!) endpoint by providing our own <b>ServerEndpointConfig<\/b> (based on <b>AnnotatedServerEndpointConfig<\/b> which <a href=\"http:\/\/www.eclipse.org\/jetty\">Jetty<\/a> kindly provides already).<\/p>\n<p>To build and run our server and clients, we need just do that:<\/p>\n<pre class=\" brush:java\">mvn clean package\r\njava -jar target\\jetty-web-sockets-jsr356-0.0.1-SNAPSHOT-server.jar \/\/ run server\r\njava -jar target\/jetty-web-sockets-jsr356-0.0.1-SNAPSHOT-client.jar \/\/ run yet another client<\/pre>\n<p>As an example, by running server and couple of clients (I run 4 of them, &#8216;<b>392f68ef<\/b>&#8216;, &#8216;<b>8e3a869d<\/b>&#8216;, &#8216;<b>ca3a06d0<\/b>&#8216;, &#8216;<b>6cb82119<\/b>&#8216;) you might see by the output in the console that each client receives all the messages from all other clients (including its own messages):<\/p>\n<pre class=\" brush:bash\">Nov 29, 2013 9:21:29 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Hello!' from 'Client'\r\nNov 29, 2013 9:21:29 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #1' from '392f68ef'\r\nNov 29, 2013 9:21:29 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #2' from '8e3a869d'\r\nNov 29, 2013 9:21:29 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #7' from 'ca3a06d0'\r\nNov 29, 2013 9:21:30 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #4' from '6cb82119'\r\nNov 29, 2013 9:21:30 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #2' from '392f68ef'\r\nNov 29, 2013 9:21:30 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #3' from '8e3a869d'\r\nNov 29, 2013 9:21:30 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #8' from 'ca3a06d0'\r\nNov 29, 2013 9:21:31 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #5' from '6cb82119'\r\nNov 29, 2013 9:21:31 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #3' from '392f68ef'\r\nNov 29, 2013 9:21:31 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #4' from '8e3a869d'\r\nNov 29, 2013 9:21:31 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #9' from 'ca3a06d0'\r\nNov 29, 2013 9:21:32 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #6' from '6cb82119'\r\nNov 29, 2013 9:21:32 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #4' from '392f68ef'\r\nNov 29, 2013 9:21:32 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #5' from '8e3a869d'\r\nNov 29, 2013 9:21:32 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #10' from 'ca3a06d0'\r\nNov 29, 2013 9:21:33 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #7' from '6cb82119'\r\nNov 29, 2013 9:21:33 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #5' from '392f68ef'\r\nNov 29, 2013 9:21:33 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #6' from '8e3a869d'\r\nNov 29, 2013 9:21:34 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #8' from '6cb82119'\r\nNov 29, 2013 9:21:34 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #6' from '392f68ef'\r\nNov 29, 2013 9:21:34 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #7' from '8e3a869d'\r\nNov 29, 2013 9:21:35 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #9' from '6cb82119'\r\nNov 29, 2013 9:21:35 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #7' from '392f68ef'\r\nNov 29, 2013 9:21:35 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #8' from '8e3a869d'\r\nNov 29, 2013 9:21:36 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #10' from '6cb82119'\r\nNov 29, 2013 9:21:36 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #8' from '392f68ef'\r\nNov 29, 2013 9:21:36 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #9' from '8e3a869d'\r\nNov 29, 2013 9:21:37 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #9' from '392f68ef'\r\nNov 29, 2013 9:21:37 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #10' from '8e3a869d'\r\nNov 29, 2013 9:21:38 PM com.example.services.BroadcastClientEndpoint onMessage\r\nINFO: Received message 'Message #10' from '392f68ef'\r\n2013-11-29 21:21:39.260:INFO:oejwc.WebSocketClient:main: Stopped org.eclipse.jetty.websocket.client.WebSocketClient@3af5f6dc<\/pre>\n<p>Awesome! I hope this introductory blog post shows how easy it became to use modern web communication protocols in Java, thanks to <a href=\"http:\/\/jcp.org\/en\/jsr\/detail?id=356\">Java WebSockets (JSR-356)<\/a>, <a href=\"http:\/\/jcp.org\/en\/jsr\/detail?id=353\">Java API for JSON Processing (JSR-353)<\/a> and great projects such as <a href=\"http:\/\/www.eclipse.org\/jetty\/documentation\/current\/jetty-javaee.html\">Jetty 9.1<\/a>!<\/p>\n<ul>\n<li>As always, complete project is available on <a href=\"https:\/\/github.com\/reta\/jetty-web-sockets-jsr356\">GitHub<\/a>.<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<div style=\"border: 1px solid #D8D8D8; background: #FAFAFA; width: 100%; padding-left: 5px;\"><b><i>Reference: <\/i><\/b><a href=\"http:\/\/aredko.blogspot.com\/2013\/11\/java-websockets-jsr-356-on-jetty-91.html\">Java WebSockets (JSR-356) on Jetty 9.1<\/a> from our <a href=\"http:\/\/www.javacodegeeks.com\/jcg\">JCG partner<\/a> Andrey Redko at the <a href=\"http:\/\/aredko.blogspot.com\/\">Andriy Redko {devmind} <\/a> blog.<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Jetty 9.1 is finally released, bringing Java WebSockets (JSR-356) to non-EE environments. It&#8217;s awesome news and today&#8217;s post will be about using this great new API along with Spring Framework. JSR-356 defines concise, annotation-based model to allow modern Java web applications easily create bidirectional communication channels using WebSockets API. It covers not only server-side, but &hellip;<\/p>\n","protected":false},"author":141,"featured_media":96,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[375,399],"class_list":["post-19358","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-enterprise-java","tag-codehaus-jetty","tag-websockets"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Java WebSockets (JSR-356) on Jetty 9.1<\/title>\n<meta name=\"description\" content=\"Jetty 9.1 is finally released, bringing Java WebSockets (JSR-356) to non-EE environments. It&#039;s awesome news and today&#039;s post will be about using this\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.javacodegeeks.com\/2013\/12\/java-websockets-jsr-356-on-jetty-9-1.html\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Java WebSockets (JSR-356) on Jetty 9.1\" \/>\n<meta property=\"og:description\" content=\"Jetty 9.1 is finally released, bringing Java WebSockets (JSR-356) to non-EE environments. It&#039;s awesome news and today&#039;s post will be about using this\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.javacodegeeks.com\/2013\/12\/java-websockets-jsr-356-on-jetty-9-1.html\" \/>\n<meta property=\"og:site_name\" content=\"Java Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/javacodegeeks\" \/>\n<meta property=\"article:published_time\" content=\"2013-12-03T17:00:44+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/codehaus-jetty-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Andrey Redko\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@javacodegeeks\" \/>\n<meta name=\"twitter:site\" content=\"@javacodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Andrey Redko\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"11 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/12\\\/java-websockets-jsr-356-on-jetty-9-1.html#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/12\\\/java-websockets-jsr-356-on-jetty-9-1.html\"},\"author\":{\"name\":\"Andrey Redko\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/771a6504862edc45322776832cbce413\"},\"headline\":\"Java WebSockets (JSR-356) on Jetty 9.1\",\"datePublished\":\"2013-12-03T17:00:44+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/12\\\/java-websockets-jsr-356-on-jetty-9-1.html\"},\"wordCount\":916,\"commentCount\":2,\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/12\\\/java-websockets-jsr-356-on-jetty-9-1.html#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/codehaus-jetty-logo.jpg\",\"keywords\":[\"Codehaus Jetty\",\"WebSockets\"],\"articleSection\":[\"Enterprise Java\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/12\\\/java-websockets-jsr-356-on-jetty-9-1.html#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/12\\\/java-websockets-jsr-356-on-jetty-9-1.html\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/12\\\/java-websockets-jsr-356-on-jetty-9-1.html\",\"name\":\"Java WebSockets (JSR-356) on Jetty 9.1\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/12\\\/java-websockets-jsr-356-on-jetty-9-1.html#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/12\\\/java-websockets-jsr-356-on-jetty-9-1.html#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/codehaus-jetty-logo.jpg\",\"datePublished\":\"2013-12-03T17:00:44+00:00\",\"description\":\"Jetty 9.1 is finally released, bringing Java WebSockets (JSR-356) to non-EE environments. It's awesome news and today's post will be about using this\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/12\\\/java-websockets-jsr-356-on-jetty-9-1.html#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/12\\\/java-websockets-jsr-356-on-jetty-9-1.html\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/12\\\/java-websockets-jsr-356-on-jetty-9-1.html#primaryimage\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/codehaus-jetty-logo.jpg\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/codehaus-jetty-logo.jpg\",\"width\":150,\"height\":150},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2013\\\/12\\\/java-websockets-jsr-356-on-jetty-9-1.html#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Java\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/java\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Enterprise Java\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/java\\\/enterprise-java\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"Java WebSockets (JSR-356) on Jetty 9.1\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"name\":\"Java Code Geeks\",\"description\":\"Java Developers Resource Center\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"alternateName\":\"JCG\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.javacodegeeks.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/javacodegeeks\",\"https:\\\/\\\/x.com\\\/javacodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/771a6504862edc45322776832cbce413\",\"name\":\"Andrey Redko\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/16419ce8394173028eddaeb992859862bab50cfcf74589fa9bb9a3dd8bb27518?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/16419ce8394173028eddaeb992859862bab50cfcf74589fa9bb9a3dd8bb27518?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/16419ce8394173028eddaeb992859862bab50cfcf74589fa9bb9a3dd8bb27518?s=96&d=mm&r=g\",\"caption\":\"Andrey Redko\"},\"description\":\"Andriy is a well-grounded software developer with more then 12 years of practical experience using Java\\\/EE, C#\\\/.NET, C++, Groovy, Ruby, functional programming (Scala), databases (MySQL, PostgreSQL, Oracle) and NoSQL solutions (MongoDB, Redis).\",\"sameAs\":[\"http:\\\/\\\/aredko.blogspot.com\\\/\",\"http:\\\/\\\/ca.linkedin.com\\\/in\\\/aredko\"],\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/author\\\/andrey-redko\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Java WebSockets (JSR-356) on Jetty 9.1","description":"Jetty 9.1 is finally released, bringing Java WebSockets (JSR-356) to non-EE environments. It's awesome news and today's post will be about using this","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.javacodegeeks.com\/2013\/12\/java-websockets-jsr-356-on-jetty-9-1.html","og_locale":"en_US","og_type":"article","og_title":"Java WebSockets (JSR-356) on Jetty 9.1","og_description":"Jetty 9.1 is finally released, bringing Java WebSockets (JSR-356) to non-EE environments. It's awesome news and today's post will be about using this","og_url":"https:\/\/www.javacodegeeks.com\/2013\/12\/java-websockets-jsr-356-on-jetty-9-1.html","og_site_name":"Java Code Geeks","article_publisher":"https:\/\/www.facebook.com\/javacodegeeks","article_published_time":"2013-12-03T17:00:44+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/codehaus-jetty-logo.jpg","type":"image\/jpeg"}],"author":"Andrey Redko","twitter_card":"summary_large_image","twitter_creator":"@javacodegeeks","twitter_site":"@javacodegeeks","twitter_misc":{"Written by":"Andrey Redko","Est. reading time":"11 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.javacodegeeks.com\/2013\/12\/java-websockets-jsr-356-on-jetty-9-1.html#article","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/2013\/12\/java-websockets-jsr-356-on-jetty-9-1.html"},"author":{"name":"Andrey Redko","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/771a6504862edc45322776832cbce413"},"headline":"Java WebSockets (JSR-356) on Jetty 9.1","datePublished":"2013-12-03T17:00:44+00:00","mainEntityOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2013\/12\/java-websockets-jsr-356-on-jetty-9-1.html"},"wordCount":916,"commentCount":2,"publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"image":{"@id":"https:\/\/www.javacodegeeks.com\/2013\/12\/java-websockets-jsr-356-on-jetty-9-1.html#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/codehaus-jetty-logo.jpg","keywords":["Codehaus Jetty","WebSockets"],"articleSection":["Enterprise Java"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.javacodegeeks.com\/2013\/12\/java-websockets-jsr-356-on-jetty-9-1.html#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.javacodegeeks.com\/2013\/12\/java-websockets-jsr-356-on-jetty-9-1.html","url":"https:\/\/www.javacodegeeks.com\/2013\/12\/java-websockets-jsr-356-on-jetty-9-1.html","name":"Java WebSockets (JSR-356) on Jetty 9.1","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2013\/12\/java-websockets-jsr-356-on-jetty-9-1.html#primaryimage"},"image":{"@id":"https:\/\/www.javacodegeeks.com\/2013\/12\/java-websockets-jsr-356-on-jetty-9-1.html#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/codehaus-jetty-logo.jpg","datePublished":"2013-12-03T17:00:44+00:00","description":"Jetty 9.1 is finally released, bringing Java WebSockets (JSR-356) to non-EE environments. It's awesome news and today's post will be about using this","breadcrumb":{"@id":"https:\/\/www.javacodegeeks.com\/2013\/12\/java-websockets-jsr-356-on-jetty-9-1.html#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.javacodegeeks.com\/2013\/12\/java-websockets-jsr-356-on-jetty-9-1.html"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/2013\/12\/java-websockets-jsr-356-on-jetty-9-1.html#primaryimage","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/codehaus-jetty-logo.jpg","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/codehaus-jetty-logo.jpg","width":150,"height":150},{"@type":"BreadcrumbList","@id":"https:\/\/www.javacodegeeks.com\/2013\/12\/java-websockets-jsr-356-on-jetty-9-1.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.javacodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"Java","item":"https:\/\/www.javacodegeeks.com\/category\/java"},{"@type":"ListItem","position":3,"name":"Enterprise Java","item":"https:\/\/www.javacodegeeks.com\/category\/java\/enterprise-java"},{"@type":"ListItem","position":4,"name":"Java WebSockets (JSR-356) on Jetty 9.1"}]},{"@type":"WebSite","@id":"https:\/\/www.javacodegeeks.com\/#website","url":"https:\/\/www.javacodegeeks.com\/","name":"Java Code Geeks","description":"Java Developers Resource Center","publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"alternateName":"JCG","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.javacodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.javacodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.javacodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/javacodegeeks","https:\/\/x.com\/javacodegeeks"]},{"@type":"Person","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/771a6504862edc45322776832cbce413","name":"Andrey Redko","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/16419ce8394173028eddaeb992859862bab50cfcf74589fa9bb9a3dd8bb27518?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/16419ce8394173028eddaeb992859862bab50cfcf74589fa9bb9a3dd8bb27518?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/16419ce8394173028eddaeb992859862bab50cfcf74589fa9bb9a3dd8bb27518?s=96&d=mm&r=g","caption":"Andrey Redko"},"description":"Andriy is a well-grounded software developer with more then 12 years of practical experience using Java\/EE, C#\/.NET, C++, Groovy, Ruby, functional programming (Scala), databases (MySQL, PostgreSQL, Oracle) and NoSQL solutions (MongoDB, Redis).","sameAs":["http:\/\/aredko.blogspot.com\/","http:\/\/ca.linkedin.com\/in\/aredko"],"url":"https:\/\/www.javacodegeeks.com\/author\/andrey-redko"}]}},"_links":{"self":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/19358","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/users\/141"}],"replies":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/comments?post=19358"}],"version-history":[{"count":0,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/19358\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media\/96"}],"wp:attachment":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media?parent=19358"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/categories?post=19358"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/tags?post=19358"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}