Friday Jan 24, 2014

Updating Tyrus in Glassfish

This article is inspired by similar one about Jersey and will provide similar information. Thanks to Michal for creating such comprehensive instructions.

Fortunately, Tyrus does not depend on HK2 so the task here is lot easier. To be absolutely honest, I did expect some issues with Grizzly dependency in Tyrus client, but changes are backwards compatible (applies to Tyrus 1.4), so you can update Tyrus to any version of released Glassfish very easily.

Which version of Tyrus am I using?

You can get this from tyrus-core.jar manifest:

1
2
$ unzip -p $GLASSFISH_HOME/glassfish/modules/tyrus-core.jar META-INF/MANIFEST.MF | grep Bundle-Version
Bundle-Version: 1.0.0

This means you are using Tyrus 1.0. I strongly recommend to upgrade. Latest version now is Tyrus 1.4 and the output will look like:

1
2
$ unzip -p $GLASSFISH_HOME/glassfish/modules/tyrus-core.jar META-INF/MANIFEST.MF | grep Bundle-Version
Bundle-Version: 1.4.0

Glassfish distributions

Table below contains overview of current Glassfish 4.x builds:

Glassfish version Download link Tyrus version
4.0 (Java EE 7 RI) [download] 1.0
4.0.1 b01 [download] 1.0
4.0.1 b02 [download] 1.2.1
4.0.1 b03 [download] 1.2.1
4.0.1 latest nightly [download] latest

Updating to Tyrus 1.4

1
2
3
4
5
6
7
8
9
10
11
$ rm $GLASSFISH_HOME/glassfish/modules/tyrus-*jar
$ unzip -j ./websocket-ri-archive-1.4.zip "websocket-ri-archive-1.4/lib/*" -d $GLASSFISH_HOME/glassfish/modules/
Archive:  ./websocket-ri-archive-1.4.zip
inflating: [path]/modules/tyrus-client-1.4.jar
inflating: [path]/modules/tyrus-container-glassfish-cdi-1.4.jar
inflating: [path]/modules/tyrus-container-grizzly-client-1.4.jar
inflating: [path]/modules/tyrus-container-servlet-1.4.jar
inflating: [path]/modules/tyrus-core-1.4.jar
inflating: [path]/modules/tyrus-server-1.4.jar
inflating: [path]/modules/tyrus-spi-1.4.jar

And that’s it. Remember to restart Glassfish instance after replacing Tyrus jar files.

Note

As of now (1/24/2013) latest nightly build of Glassfish contains Tyrus 1.3.3. Next nightly should contain latest Tyrus release – version 1.4.

Links

Tuesday Jan 21, 2014

WebSocket Client on Android – Tyrus

Running some Java EE libraries or frameworks in non-standard VM is not an uncommon task and same is for Tyrus. I have to admit that this task was driven mainly by issue report TYRUS-256 from Reza. There is no official support from Tyrus running on Dalvik and I'm not even sure if it is a good idea, but important fact is that it works and you can do it if you want :-).

Whole issue which blocked runtime from being able to run on Android was in usage of javax.naming.InitialContext class. There is no simple alternative to it, but fortunately there is always a possibility to do little bit of reflection hacking to get rid of the dependency if it's not there. The rest was only about creating the sample application and testing it on my phone, which was not that hard. I have to give kudos to IntelliJ IDEA team for their support, but not for Android Studio - it uses gradle as build tool and it seems like you cannot include java library because android plugin clashes with java plugin (I'm not very familiar with gradle as you might have noticed). Using and build script and build in support was better for my task.

The application I used for testing is available on github in my personal workspace: https://github.com/pavelbucek/tyrus-client-android-test. Feel free to test it and/or provide pull requests. I would be particularly interested in gradle build script.

That's it for today. If you are using Tyrus on Android or if you have any related comments, please share them with us on Tyrus mailing list or as an enhancement request.

Thursday Jan 16, 2014

Tyrus 1.4

I’m pleased to announce that Tyrus 1.4 was released today. It contains all features mentioned recently on my blog: Extensions support (including compression extensions implementation), shared client container and many other improvements and bug fixes. Binaries will be available on maven central repository soon and Tyrus 1.4 will be integrated into Glassfish trunk.

As always, feel free to contribute! You can file an enhancement or a bug or just ask if there is something unclear in the documentation.

Release notes

Bugs

  • [TYRUS-136] – org.glassfish.tyrus.server.Server does not stop correctly when DeploymentException is thrown
  • [TYRUS-263] – SSL via HTTP PROXY – wss://echo.websocket.org Handshake error. “Response code was not 101: 200″
  • [TYRUS-269] – Parallel connection to ServerEndpoint with URI template mix up response to client instances …
  • [TYRUS-270] – The title of file README.html of Draw sample is mis-typed from “Draw Sample” to “Chat Sample”
  • [TYRUS-271] – “?null” is added to every request without query params
  • [TYRUS-272] – TyrusServerConfiguration incompatible with jetty’s jsr 356 implementation.
  • [TYRUS-273] – EJB component provider needs to provide method which will be invoked.
  • [TYRUS-275] – Tyrus client has bug causing limitation of # of open web sockets
  • [TYRUS-276] – Session accepts messages after idle timeout (@OnMessage is triggered)
  • [TYRUS-277] – session.setMaxIdleTimeout(0) does not reset/cancel the timeout
  • [TYRUS-280] – Extension parsed does not allow parameter without value
  • [TYRUS-281] – Client does CDI/EJB lookup
  • [TYRUS-282] – Session.setMaxIdleTimeout(..) does not work as expected for negative values
  • [TYRUS-288] – WebSocket connections are automatically closed after 30 idle seconds in standalone mode

Improvements

  • [TYRUS-56] – content root directory configuration in Server class for static content
  • [TYRUS-160] – Tyrus tests won’t compile with JDK 1.6
  • [TYRUS-183] – UTF8 validation logic should be separated from frame parsing
  • [TYRUS-265] – Improve GrizzlyWriter implementation
  • [TYRUS-266] – Support for WebSocket extensions
  • [TYRUS-268] – In-memory transport for testing / performance analysis

New Features

  • [TYRUS-193] – Support Tyrus with different transports
  • [TYRUS-283] – CompressionExtension (permessage-compression)
  • [TYRUS-286] – Shared client container improvement (stop when there is no open session)
  • [TYRUS-287] – Cannot create non-daemon threads with Tyrus server standalone mode

Tasks

  • [TYRUS-246] – Investigate and fix issues related to ServletTest#testWebSocketBroadcast
  • [TYRUS-264] – Client SPI

Wednesday Jan 08, 2014

WebSocket Extensions in Tyrus

There is always room for another experimental feature :-) This one is maybe little less experimental than broadcast support, but please implement presented APIs with one important fact in your mind – it can change any time.

What is WebSocket Extension?

You can think of WebSocket Extension as a filter, which processes all incoming and outgoing frames. Frame is the smallest unit in WebSocket protocol which can be transferred on the wire – it contains some some metadata (frame type, opcode, payload length, etc.) and of course payload itself.

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+

Figure taken from RFC 6455

If you are interested about some more details related to WebSocket protocol specification, please see linked RFC document (RFC 6455).

What can be achieved by WebSocket Extension?

Almost everything. You can change every single bit of incoming or outgoing frame, including control frames (close, ping and pong). There are some RFC drafts trying to standardise extensions like per message compression (used to be per frame compression) and multiplexing extension (now expired).

Tyrus already has support for per message compression extension and exposes interfaces which allow users to write custom extensions with completely different functionality.

When should I consider implementing WebSocket Extensions?

This is maybe the most important question. WebSocket Extensions can do almost everything, but you should not use them for use cases achievable by other means. Why? Majority of WebSocket use cases are about communication with browsers and javascript client cannot really influence which exception is going to be used. Browser must support your particular extension (by default or it can be enabled by some custom module).

You can easily use custom extension when using Tyrus java client, so if there is no browser interaction in your application, it should be easier to distribute your extensions to involved parties and you might lift the threshold when deciding whether something will be done by extension or by application logic.

Java API for WebSocket and Extensions

API currently contains following extension representation (javadoc removed):

1
2
3
4
5
6
7
8
9
10
public interface Extension {
String getName();
List getParameters();
interface Parameter {
String getName();
String getValue();
}
}

and the specification (JSR 356) limits extension definition only for handshake purposes. To sum that up, users can only declare Extension with static parameters (no chance to set parameters based on request extension parameters) and that’s it. These extensions don’t have any processing part, so the work must be done somewhere else. As you might already suspect, this is not ideal state. Usability of extensions specified like this is very limited, it is basically just a marker class which has some influence on handshake headers. You can get list of negotiated extensions in the runtime (Session.getNegotiatedExtensions()) but there is no way how you could access frame fields other than payload itself.

Proposed Extension API

I have to repeat warning already presented at the start of this blog post – anything mentioned below might be changed without notice. There are some TODO items which will most likely require some modification of presented API, not to mention that RFC drafts of WebSocket Extensions are not final yet. There might be even bigger modification needed – for example, multiplexing draft specifies different frame representation, use of RSV bits is not standardised etc. So please take following as a usable proof of concept and feel free to use them in agile projects.

Firstly, we need to create frame representation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Frame {
public boolean isFin() { .. }
public boolean isRsv1() { .. }
public boolean isRsv2() { .. }
public boolean isRsv3() { .. }
public boolean isMask() { .. }
public byte getOpcode() { .. }
public long getPayloadLength() { .. }
public int getMaskingKey() { .. }
public byte[] getPayloadData() { .. }
public boolean isControlFrame() { .. }
public static Builder builder() { .. }
public static Builder builder(Frame frame) { .. }
public final static class Builder {
public Builder() { .. }
public Builder(Frame frame) { .. }
public Frame build() { .. }
public Builder fin(boolean fin) { .. }
public Builder rsv1(boolean rsv1) { .. }
public Builder rsv2(boolean rsv2) { .. }
public Builder rsv3(boolean rsv3) { .. }
public Builder mask(boolean mask) { .. }
public Builder opcode(byte opcode) { .. }
public Builder payloadLength(long payloadLength) { .. }
public Builder maskingKey(int maskingKey) { .. }
public Builder payloadData(byte[] payloadData) { .. }
}
}

This is pretty much straightforward copy of frame definition mentioned earlier. Frame is designed as immutable, so you cannot change it in any way. One method might be recognised as mutable – getPayloadData() – returns modifiable byte array, but it is always new copy, so the original frame instance remains intact. There is also a Frame.Builder for constructing new Frame instances, notice it can copy existing frame, so creating a new frame with let’s say RSV1 bit set to “1″ is as easy as:

1
Frame newFrame = Frame.builder(originalFrame).rsv1(true).build();

Note that there is only one convenience method: isControlFrame. Other information about frame type etc needs to be evaluated directly from opcode, simply because there might not be enough information to get the correct outcome or the information itself would not be very useful. For example: opcode 0×00 means continuation frame, but you don’t have any chance to get the information about actual type (text or binary) without intercepting data from previous frames. Consider Frame class as as raw as possible representation.isControlFrame can be also gathered from opcode, but it is at least always deterministic and it will be used by most of extension implementations. It is not usual to modify control frames as it might end with half closed connections or unanswered ping messages.

New Extension representation needs to be able to handle extension parameter negotiation and actual processing of incoming and outgoing frames. It also should be compatible with existing javax.websocket.Extension class, since we wan’t to re-use existing registration API and be able to return new extension instance included in response fromSession.getNegotiatedExtensions():List<Extension> call. Consider following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface ExtendedExtension extends Extension {
Frame processIncoming(ExtensionContext context, Frame frame);
Frame processOutgoing(ExtensionContext context, Frame frame);
List onExtensionNegotiation(ExtensionContext context, List requestedParameters);
void onHandshakeResponse(ExtensionContext context, List responseParameters);
void destroy(ExtensionContext context);
interface ExtensionContext {
Map<String, Object> getProperties();
}
}

ExtendedExtension is capable of processing frames and influence parameter values during the handshake. Extension is used on both client and server side and since the negotiation is only place where this fact applies, we needed to somehow differentiate these sides. On server side, only onExtensionNegotiation(..) method is invoked and client side hasonHandshakeResponse(..). Server side method is a must, client side could be somehow solved by implementing ClientEndpointConfig.Configurator#afterResponse(..) or calling Session.getNegotiatedExtenions(), but it won’t be as easy to get this information back to extension instance and even if it was, it won’t be very elegant. Also, you might suggest replacing processIncoming and processOutgoing methods by just oneprocess(Frame) method. That is also possible, but then you might have to assume current direction from frame instance or somehow from ExtenionContext, which is generally not a bad idea, but it resulted it slightly less readable code.

Last but not least is ExtensionContext itself and related lifecycle method. OriginalExtension from javax.websocket is singleton and ExtendedExtension must obey this fact. But it does not meet some requirements we stated previously, like per connection parameter negotiation and of course processing itself will most likely have some connection state. Lifecycle of ExtensionContext is defined as follows: ExtensionContextinstance is created right before onExtensionNegotiation (server side) oronHandshakeResponse (client side) and destroyed after destroy method invocation. Obviously, processIncoming or processOutgoing cannot be called before ExtensionContextis created or after is destroyed. You can think of handshake related methods as @OnOpenand destroy as @OnClose.

For those more familiar with WebSocket protocol: process*(ExtensionContext, Frame) is always invoked with unmasked frame, you don’t need to care about it. On the other side, payload is as it was received from the wire, before any validation (UTF-8 check for text messages). This fact is particularly important when you are modifying text message content, you need to make sure it is properly encoded in relation to other messages, because encoding/decoding process is stateful – remainder after UTF-8 coding is used as input to coding process for next message. If you want just test this feature and save yourself some headaches, don’t modify text message content or try binary messages instead.

Code sample

Let’s say we want to create extension which will encrypt and decrypt first byte of every binary message. Assume we have a key (one byte) and our symmetrical cipher will be XOR. (Just for simplicity (a XOR key XOR key) = a, so encrypt() and decrypt() functions are the same).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class CryptoExtension implements ExtendedExtension {
@Override
public Frame processIncoming(ExtensionContext context, Frame frame) {
return lameCrypt(context, frame);
}
@Override
public Frame processOutgoing(ExtensionContext context, Frame frame) {
return lameCrypt(context, frame);
}
private Frame lameCrypt(ExtensionContext context, Frame frame) {
if(!frame.isControlFrame() && (frame.getOpcode() == 0x02)) {
final byte[] payloadData = frame.getPayloadData();
payloadData[0] ^= (Byte)(context.getProperties().get("key"));
return Frame.builder(frame).payloadData(payloadData).build();
} else {
return frame;
}
}
@Override
public List onExtensionNegotiation(ExtensionContext context,
List requestedParameters) {
init(context);
// no params.
return null;
}
@Override
public void onHandshakeResponse(ExtensionContext context,
List responseParameters) {
init(context);
}
private void init(ExtensionContext context) {
context.getProperties().put("key", (byte)0x55);
}
@Override
public void destroy(ExtensionContext context) {
context.getProperties().clear();
}
@Override
public String getName() {
return "lame-crypto-extension";
}
@Override
public List getParameters() {
// no params.
return null;
}
}

You can see that ExtendedExtension is slightly more complicated that original Extension so the implementation has to be also not as straightforward.. on the other hand, it does something. Sample code above shows possible simplification mentioned earlier (one process method will be enough), but please take this as just sample implementation. Real world case is usually more complicated.

Now when we have our CryptoExtension implemented, we want to use it. There is nothing new compared to standard WebSocket Java API, feel free to skip this part if you are already familiar with it. Only programmatic version will be demonstrated. It is possible to do it for annotated version as well, but it is little bit more complicated on the server side and I want to keep the code as compact as possible.

Client registration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ArrayList extensions = new ArrayList();
extensions.add(new CryptoExtension());
final ClientEndpointConfig clientConfiguration =
ClientEndpointConfig.Builder.create()
.extensions(extensions).build();
WebSocketContainer client = ContainerProvider.getWebSocketContainer();
final Session session = client.connectToServer(new Endpoint() {
@Override
public void onOpen(Session session, EndpointConfig config) {
// ...
}
}, clientConfiguration, URI.create(/* ... */));

Server registration:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class CryptoExtensionApplicationConfig implements ServerApplicationConfig {
@Override
public Set getEndpointConfigs(Set<Class<? extends Endpoint>> endpointClasses) {
Set endpointConfigs = new HashSet();
endpointConfigs.add(
ServerEndpointConfig.Builder.create(EchoEndpoint.class, "/echo")
.extensions(Arrays.asList(new CryptoExtension())).build()
);
return endpointConfigs;
}
@Override
public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {
// all scanned endpoints will be used.
return scanned;
}
}
public class EchoEndpoint extends Endpoint {
@Override
public void onOpen(Session session, EndpointConfig config) {
// ...
}
}

CryptoExtensionApplicationConfig will be found by servlets scanning mechanism and automatically used for application configuration, no need to add anything (or even have)web.xml.

Per Message Deflate Extension

The original goal of whole extension support was to implement Permessage extension as defined in draft-ietf-hybi-permessage-compression-15 and we were able to achieve that goal. Well, not completely, current implementation ignores parameters. But it seems like it does not matter much, it was tested with Chrome and it works fine. Also it passes newest version of Autobahn test suite, which includes tests for this extension.

  1. PerMessageDeflateExtension.java (compatible with draft-ietf-hybi-permessage-compression-15, autobahn test suite)
  2. XWebKitDeflateExtension.java (compatible with Chrome and Firefox – same as previous, just different extension name)
  3. PerMessageDeflateTest.java

TODO

There are some things which needs to be improved or specified to make this reliable and suitable for real world use. It might not seem as big feature, but it enables lots of use cases not defined in original specification and some of them are clashing little bit, so for now, I kept everything as it was when you are not using extended extensions.

Everything mentioned in this article is already available in Tyrus 1.4-SNAPSHOT and will be part of 1.4 release.

  • Extension / Frame Validation
  • Frame representation – frame types
  • Frame representation – payload representation
  • Frame representation – masking – remove? (current state: container responsibility)
  • Exception handling – processIncoming and processOutgoing methods (current state: exceptions are logged)
  • Exception handling – onExtensionNegotiation
  • Possibility to reject negotiation extension in onExtensionNegotiation (based on extension params)
  • Extension ordering (current state: handshake response header order)
  • Extension resource validation (two extensions using same RSV bit(s) cannot be negotiated)
  • PerMessageDeflate – parameters
  • MultiplexingExtension – implement when (if) ready

Conclusion

There is still lots of decisions to be made and things to do, but it seems like we can implement usable extensions which are supported by newer versions of browsers and containers. PerMessageDeflate extension is nice example of handy feature which can save significant resources.

Links

Saturday Dec 28, 2013

Tyrus client – shared container

Current “trunk” version contains one notable feature – shared client container. It can save significant portion of resources on client side if you are creating lots of client connections and there is almost no drawback.

This feature was added as a response to TYRUS-275 and it somehow copies what other containers are doing in the JRS 356 implementation. Specification does not really state which system resources should be reusable (that’s ok, I think this is implementation detail), but it also does not take care of thread pool related settings, which might introduce some inconsistencies among implementations.

WebSocket client implementation in Tyrus re-creates client runtime wheneverWebSocketContainer#connectToServer is invoked. This approach gives us some perks like out-of-the-box isolation and relatively low thread count (currently we have 1 selector thread and 2 worker threads). Also it gives you the ability to stop the client runtime – one Session instance is tied to exactly one client runtime, so we can stop it when Session is closed. This seems as a good solution for most of WebSocket client use cases – you usually use java client from application which uses it for communicating with server side and you typically don’t need more than 10 instances (my personal estimate is that more than 90% applications won’t use more than 1 connection). There are several reasons for it – of it is just a client, it needs to preserve server resources – one WebSocket connection means one TCP connection and we don’t really want clients to consume more than needed. Previous statement may be invalidated by WebSocket multiplexing extension, but for now, it is still valid.

On the other hand, WebSocket client implementations in some other containers took another (also correct) approach – they share client runtime for creating all client connections. That means they might not have this strict one session one runtime policy, they cannot really give user way how he to control system resources, but surely it has another advantage – it can handle much more opened connections. Thread pools are share among client sessions which may or may not have some unforeseen consequences, but if its implemented correctly, it should outperform Tyrus solution mentioned in previous paragraph in some use cases, like the one mentioned in TYRUS-275 - performance tests. Reported created simple program which used WebSocket API to create clients and connect to remote endpoint and he measured how many clients can he create (or in other words: how many parallel client connection can be created; I guess that original test case is to measure possible number of concurrent clients on server side, but that does not really matter for this post). Tyrus implementation loose compared to some other and it was exactly because it did not have shared client runtime capability.

I hope that common grounds are established and explained and we can take more detailed look to Tyrus implementation of shared client runtime. Default client implementation is using Grizzly as client container and all the work was done there – client SPI did not need to be changed (phew), so basically there are only some static fields and some basic synchronisation required for this to work. Grizzly part was really easy, it is only about sharing the instance of created TCPNIOTransport. Another small enhancement related to this feature is shared container timeout – if shared client runtime does not handle any connections for set period of time, it will just shutdown and clear all used resources. Everything will be automatically recreated when needed. Default value for this is 30 seconds.

How can you use this feature?

1
2
3
ClientManager client = ClientManager.createClient();
client.getProperties().put(GrizzlyClientContainer.SHARED_CONTAINER, true);

And you are done. You might also want to specify container idle timeout:

1
client.getProperties().put(GrizzlyClientContainer.SHARED_CONTAINER_IDLE_TIMEOUT, 5);

and last but not least, you might want to specify thread pool sizes used by shared container (please use this feature only when you do know what are you doing. Grizzly by default does not limit max number of used threads, so if you do that, please make sure thread pool size fits your purpose):

1
2
client.getProperties().put(GrizzlyClientSocket.SELECTOR_THREAD_POOL_CONFIG, ThreadPoolConfig.defaultConfig().setMaxPoolSize(3));
client.getProperties().put(GrizzlyClientSocket.WORKER_THREAD_POOL_CONFIG, ThreadPoolConfig.defaultConfig().setMaxPoolSize(10));

And that’s it for today. You can use mentioned features with Tyrus 1.4-SNAPSHOT and newer, feel free to comment this post or drop me a note to users@tyrus.java.net with any feedback or suggestion how this can be improved.

Links

Wednesday Dec 18, 2013

Want to work on Tyrus? We are hiring!

Tyrus project is looking for another contributor! If you want to became part of the team which is working on WebSocket API for Java (JSR 356) Reference implementation and related projects and products, this is the time to update your CV and share it with us. This job position is based in Prague, Czech Republic.

Formal job offer can be found on linked in.

If you are interested and have any questions, feel free to contact me directly via this blog or via pavel.bucek at oracle.com.

Tuesday Nov 19, 2013

Optimized WebSocket broadcast

Broadcast scenario is one of the most common use cases for WebSocket server-side code, so we are going to evaluate usability of current version of WebSocket API for Java to do that and suggest some improvements.

Please note that this post is experimental by its nature. You can use Tyrus features mentioned in this article, but anything can change any time, without warning.

When speaking about broadcast, let's define what that actually is. When message is broadcasted,  it means that it is sent to all connected clients. Easy right? Common WebSocket sample application is chat (Tyrus is not an exception, see ./samples/chat) which does exactly that - when client sends a message to server endpoint, message gets re-sent to all clients so they can see it.

If we ignore authentication and authorisation, we can shrink server-side implementation to following code:

@OnMessage
public void onMessage(Session s, String m) throws IOException {
  for (Session session : s.getOpenSessions()) {
    session.getBasicRemote().sendText(m);
   }
}

Which works well and provides expected functionality. Underlying code must process the message for every connected client, so the DataFrame which will be sent on the is constructed n-times (where n is number of connected clients). Everything now depends on processing time required for creating single data frame. That operation is not expensive per say, but just the fact it needs to be invoked that many times creates a bottle neck from it. Another important fact is that the WebSocket connection has no state, so once created data frame can be sent to as many clients as you want. So in another words, we don't really need to do create data frame multiple times, especially when we know that the message is the same for all connected clients.

WebSocket API does not allow consumers to access messages on data frame level and also does not provide way how to send already constructed data frame. That might come in some next version of the specification... so what can we do now?

If you are using Tyrus (1.3 or newer), you can try an optimized version of the same use-case:

@OnMessage
public void onMessage(Session s, String m) {
  ((TyrusSession) s).broadcast(m);
}

This way, data frame will be constructed only once which will save server-side resources and additionally clients will receive broadcasted message in shorter period. "broadcast" method returns Map<Session, Future<?>> which can be used for getting the info about which message was already send and which wasn't. Version with callback is not yet available, might be added later (if you'd want to have this feature, please send us a note to users@tyrus.java.net).

I don't have any exact measurements to confirm performance gain when using Tyrus broadcast, but seems like it may be significant, especially for higher count of connected clients.

(note to JDK8 users: the first scenario can also be improved by using fork/join framework. It was intentionally ignored in this article, since Tyrus need to stick with Java SE 7 for now)

If you have any questions, don't hesitate and ask at users@tyrus.java.net.

And, as always, list of related links:

Monday Nov 04, 2013

Asynchronous connectToServer

Users of JSR-356 – Java API for WebSocket are probably familiar with WebSocketContainer#connectToServer method. This article will be about its usage and improvement which was introduce in recent Tyrus release.

WebSocketContainer#connectToServer does what is says, it connects to WebSocketServerEndpoint deployed on some compliant container. It has two or three parameters (depends on which representation of client endpoint are you providing) and returns aSession. Returned Session represents WebSocket connection and you are instantly able to send messages, register MessageHandlers, etc.

An issue might appear when you are trying to create responsive user interface and use this method – its execution blocks until Session is created which usually means some container needs to be started, DNS queried, connection created (it’s even more complicated when there is some proxy on the way), etc., so nothing which might be really considered as responsive. Trivial and correct solution is to do this in another thread and monitor the result, but.. why should users do that? :-) Tyrus now provides async* versions of all connectToServer methods, which performs only simple (=fast) check in the same thread and then fires a new one and performs all other tasks there. Return type of these methods is Future<Session>.

List of added methods:

As you can see, all connectToServer variants have its async* alternative. All these methods do throw DeploymentException, same as synchronous variants, but some of these errors cannot be thrown as a result of the first method call, so you might get it as the cause ofExecutionException thrown when Future<Session>.get() is called.

Please let us know if you find these newly added methods useful or if you would like to change something (signature, functionality, …) – you can send us a comment to users@tyrus.java.net or ping me personally.

Related links:

Friday Aug 02, 2013

Securing WebSocket applications on Glassfish

Today we are going to cover deploying secured WebSocket applications on Glassfish and access to these services using WebSocket Client API.

WebSocket server application setup

Our server endpoint might look as simple as this:

@ServerEndpoint("/echo")
public class EchoEndpoint {

  @OnMessage
  public String echo(String message) {
    return message + " (from your server)";
  }
}

Everything else must be configured on container level.

We can start with enabling SSL, which will require web.xml to be added to your project. For starters, it might look as following:

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee">
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Protected resource</web-resource-name>
      <url-pattern>/*</url-pattern>
      <http-method>GET</http-method>
    </web-resource-collection>

    <!-- https -->
    <user-data-constraint>
      <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
  </security-constraint>
</web-app>

This is minimal web.xml for this task - web-resource-collection just defines URL pattern and HTTP method(s) we want to put a constraint on and user-data-constraint defines that constraint, which is in our case transport-guarantee. More information about these properties and security settings for web application can be found in Oracle Java EE 7 Tutorial.

I have some simple webpage attached as well, so I can test my endpoint right away. You can find it (along with complete project) in Tyrus workspace: [webpage] [whole project].

After deploying this application to Glassfish Application Server, you should be able to hit it using your favorite browser. URL where my application resides is https://localhost:8181/sample-echo-https/ (may be different, depends on other configuration). My browser warns me about untrusted certificate (I use what freshly built Glassfish provides - self signed certificates) and after adding an exception for this site, I can see my webpage and I am able to securely connect to wss://localhost:8181/sample-echo-https/echo.

WebSocket client

Already mentioned demo application also contains test client, but execution of this is skipped for normal build. Reason for this is that Glassfish uses these self-signed "random" untrusted certificates and you are (in most cases) not able to connect to these services without any additional settings.

Creating test WebSocket client is actually quite similar to server side, only difference is that you have to somewhere create client container and invoke connect with some additional info. Java API for WebSocket allows you to use annotated and programmatic way to construct endpoints. Server side shows the annotated case, so let's see how the programmatic approach will look.

final WebSocketContainer client = ContainerProvider.getWebSocketContainer();
client.connectToServer(new Endpoint() {
  @Override
  public void onOpen(Session session, EndpointConfig EndpointConfig) {
    try {
      // register message handler - will just print out the
      // received message on standard output.
      session.addMessageHandler(new MessageHandler.Whole<String>() {
        @Override
        public void onMessage(String message) {
          System.out.println("### Received: " + message);
        }
      });

      // send a message
      session.getBasicRemote().sendText("Do or do not, there is no try.");
    } catch (IOException e) {
      // do nothing
    }
  }
}, ClientEndpointConfig.Builder.create().build(),
   URI.create("wss://localhost:8181/sample-echo-https/echo"));

This client should work with some secured endpoint with valid certificated signed by some trusted certificate authority (you can try that with wss://echo.websocket.org). Accessing our Glassfish instance will require some additional settings. You can tell Java which certificated you trust by adding -Djavax.net.ssl.trustStore property (and few others in case you are using linked sample).

Complete command line when you are testing your service might need to look somewhat like:

mvn clean test -Djavax.net.ssl.trustStore=$AS_MAIN/domains/domain1/config/cacerts.jks\
    -Djavax.net.ssl.trustStorePassword=changeit -Dtyrus.test.host=localhost\
    -DskipTests=false

Where AS_MAIN points to your Glassfish instance.

Note: you might need to setup keyStore and trustStore per client instead of per JVM; there is a way how to do it, but it is Tyrus proprietary feature: http://tyrus.java.net/documentation/1.2.1/user-guide.html#d0e1128.

And that's it! Now nobody is able to "hear" what you are sending to or receiving from your WebSocket endpoint.

There is always room for improvement, so the next step you might want to take is introduce some authentication mechanism (like HTTP Basic or Digest). This topic is more about container configuration so I'm not going to go into details, but there is one thing worth mentioning: to access services which require authorization, you might need to put this additional information to HTTP headers of first (Upgrade) request (there is not (yet) any direct support even for these fundamental mechanisms, user need to register Configurator and add headers in beforeRequest method invocation). I filed related feature request as TYRUS-228; feel free to comment/vote if you need this functionality.

Friday Jul 26, 2013

Tyrus 1.2

UPDATE - Tyrus 1.2.1

Tyrus 1.2.1 contains fix for TYRUS-222

Original post about Tyrus 1.2

Another release cycle is finished which allows me to present Tyrus in version 1.2. This version brings some bugfixes and features, for example improved Servlet integration, correct ByteBuffer handling when sending messages, improved client-side SSL ("wss://...") support and important fixed for handling of huge messages (client and server side).

As previously - I will follow with more blog posts about selected features later. Some of them are already described in updated User guide.

Complete list of bugfixes and new features

TYRUS-216
TYRUS-206
TYRUS-207
TYRUS-208
TYRUS-209
TYRUS-211
TYRUS-106
TYRUS-199
TYRUS-215
TYRUS-154
TYRUS-217
TYRUS-218
TYRUS-197
TYRUS-210
TYRUS-146
TYRUS-219

(You might see some of these as still open, but that's due to some issues with JIRA which should be hopefully fixed soon).

Tyrus 1.2 is already integrated in Glassfish trunk – you can download nightly build or upgrade to newer Tyrus manually (replace all Tyrus jars).

Related links:

Tuesday Jul 16, 2013

WebSocket via HTTP proxy

As you might know, WebSocket can be used for bi-directional "real-time" communication with multiple clients. What does that mean in proxy environments and how this even works? WebSocket uses HTTP upgrade mechanism specified in HTTP 1.1 and by design requires open (TCP) connection.

HTTP CONNECT is there for exactly these usecases. It is usually used for tunneling HTTPS via proxy, but it can be used for WebSocket as well.

I will describe complete "proxified" handshake using captured connection of Tyrus Client connecting to public echo service - echo.websocket.org. Please note that we are directly using Tyrus API - not WebSocket specification (JSR-356), because we need to set a proxy.

final ClientManager client = ClientManager.createClient();

client.getProperties().put(
  GrizzlyClientSocket.PROXY_URI, "http://my.proxy:8080"
);
final Session session = client.connectToServer(new Endpoint() {

  @Override
  public void onOpen(Session session, EndpointConfig config) {
    session.addMessageHandler(new MessageHandler.Whole<String>() {
      @Override
      public void onMessage(String message) {
        System.out.println("# Message received: " + message);
      }
    });
  }
}, ClientEndpointConfig.Builder.create().build(),
   URI.create("ws://echo.websocket.org"));

session.getBasicRemote().sendText("test message");

BTW, Tyrus Client proxy support can be improved, currently it does not support proxy authentication, JDK's ProxySelector and so on. Please vote/comment on TYRUS-204 if you lack some of the mentioned options or anything else related to proxy support.

Current modern browsers do support this out of the box, so all you need is to set your HTTP proxy and described handshake will be done automatically. There might be limitation for parallel open connection in browser tab/instance, but I don't have any exact data about this.

Also, you might ask whether there is some need to server-side support - simple answer is "no". HTTP containers will see regular connection (from proxy), there is no additional work or overhead on that side.

Lets see our dumped communication:

client > proxy
CONNECT echo.websocket.org:80 HTTP/1.1
Host: echo.websocket.org
Proxy-Connection: keep-alive
Connection: keep-alive

Firstly, client need to send a request to proxy for new "permanent" connection. As already mentioned, CONNECT method handles this. First argument is a hostname (echo.websocket.org) and standard HTTP version.

proxy > client
HTTP/1.0 200 Connection established

If you are lucky, your proxy does support CONNECT and allows you to create connection (HTTP 200 is returned).

client > proxy
GET / HTTP/1.1
Connection: Upgrade
Host: echo.websocket.org
Origin: echo.websocket.org
Sec-WebSocket-Key : sDD3Wk7PMRCPE9+C0VyOcQ==
Sec-WebSocket-Version: 13
Upgrade: websocket

This is standard WebSocket handshake request, which will be passed to target HTTP container.

proxy > client
HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Accept: 8TNIHr7bJHqQadjXYvqLql6RFEA=
Date: Tue, 16 Jul 2013 15:30:53 GMT
...

And there is a valid response to our handshake request, connection is established and communication can be started; there is nothing else different than in proxy-less environment. Please note that proxies do have limited resources and your request may be turned down because proxy "CONNECT" pool is empty.

Conclusion here is that WebSocket can work via proxies without any limitation, it just introduces different kind of traffic than pure HTTP and might cause some additional requirements related to proxy performance in case you are going to use WebSocket for long-running client connections.

Monday Jul 08, 2013

WebSocket command line client

Tyrus 1.1 brings new feature - simple command line client, which can be useful in many scenarios. You can use it for simple development testing, sanity testing or for monitoring of your deployed endpoints.

How to use:

# you can use wget .. or anything else you like:
wget http://search.maven.org/remotecontent?filepath=org/glassfish/tyrus/ext/tyrus-client-cli/1.1/tyrus-client-cli-1.1.jar -O ./tyrus-client-cli-1.1.jar
  • Execute downloaded binary
java -jar ./tyrus-client-cli-1.1.jar --help

that should print out usage information. Tyrus CLI client currently supports sending text messages and pings to one opened endpoint + you can close current session and connect to another one within one run. (If you try to open another connection while having another one already connected, close will be invoked automatically, so you will be still talking only to one endpoint).

Example:

$ java -jar ./tyrus-client-cli-1.1.jar ws://echo.websocket.org
# Connecting to ws://echo.websocket.org...
# Connected in session e303ad22-c5af-4bc3-9384-58ce6832ae94
session e303...ae94> ping
# pong-message
session e303...ae94> send tyrus-test-message
# text-message: tyrus-test-message
session e303...ae94> close
# closed: CloseReason[1000,no reason given]
# Session closed

note: lines starting with hash '#' symbol are generated from the client runtime, "# text-message: ..." means text message was received, etc.

This utility can be improved, please let us know if you have any suggestions about what feature would you like to add.

Many thanks to Gerard Davison for contributing main functionality of this module!

Tuesday Jul 02, 2013

Tyrus 1.1

It might seem like there is not much time passed since Tyrus 1.0 (Java API for WebSocket reference implementation) release, but the fact is it was frozen several weeks before going public and development in the trunk continued. Tyrus 1.1 brings some new features and improvements:

  • client-side proxy support
  • simple command line client
  • various stability/performance fixes (see below for complete list)

Individual blog posts about highlighted features will follow, same as related user guide chapters.. stay tuned!

Tyrus 1.1 is already integrated in Glassfish trunk - you can download nightly build or upgrade to newer Tyrus manually (replace all Tyrus jars; I know this is not very user friendly, so I'll try to come up with some better solution or at least simple guide).

Complete list of bugfixes/improvements:

Related links:

Wednesday Jun 19, 2013

Tyrus 1.0

Hi all,

Java EE 7 was released last week and lots of new exciting libraries and frameworks with it. One of them is Tyrus 1.0, reference implementation of JSR-356: Java API for WebSocket.

Tyrus has new home page (thanks to whole Grizzly team to allow us use their web as a base for ours), user guide and much more. Please check it out on https://tyrus.java.net and let us know what you think on users@tyrus.java.net. We are always open for any suggestions or requests (features, tutorials, documentation, ...)!


Tyrus project has bigger ambition than just be reference implementation, so stay tuned, some cool features are planned (and already done) for Tyrus 1.1 release. If you have anything you would want to get into it, please file a bug/improvement on our issue tracker.

Links:

Friday Jun 07, 2013

Tyrus 1.0 user guide

We published first version of Tyrus user guide (thanks Stepan!) and we are looking for any feedback. I'm pretty sure current version would be sufficient for most of usecases, but there is always room for improvements.

 User guide:

Feel free to drop us a note about anything you'd expect to find there to users@tyrus.java.net. Thanks!




About

Pavel Bucek-Oracle

Search

Categories
Archives
« May 2015
SunMonTueWedThuFriSat
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
      
Today